ECMAScript 不需 Type Annotation 與 Type Coercion 是優點也是缺點,優點是 Codebase 很精簡,不用夾雜 Type 資訊;缺點是這些常是 Bug 來源。Sanctuary 支援 Runtime Type Checking,在 Type 上遠比 Ramda 嚴謹。
Version
Sanctuary 3.1.0
Ramda 0.27.1
Ramda
import { add } from 'ramda'
add(2)(2) // ?
add(2)(true) // ?
以 Ramda 的 add()
為例,其 type signature 明明為 Number -> Number -> Number
,當你不小心傳進 true
依然可執行且有結果,因為 true
會 type corecion 成 1
,因此結果為 3
。
Sanctuary
import { add } from 'sanctuary'
add(2)(2) // ?
add(2)(true) // ?
Sanctuary 預設會啟動 type checking,因此傳入 true
會直接 runtime error,而不會 type coercion。
import { create, env } from 'sanctuary'
let { add } = create({ checkTypes: false, env })
add(2)(2) // ?
add(2)(true) // ?
若想取消 type checking,則需改用 create()
建立 add()
,並將 checkTypes
設定為 false
。
Type Checking
import { create, env, Number } from 'sanctuary-def'
let def = create({ checkTypes: true, env })
let add = def('add')({})([Number, Number, Number]) (
x => y => x + y
)
add(2)(2) // ?
add(2)(true) // ?
事實上自己寫的 function,也能借用 Sanctuary 的 type checking 保護。
第 1 行
import { create, env, Number } from 'sanctuary-def'
由 sanctuary-def
引入 create()
與 env
,將由 create()
定義建立 def()
,env
則包含 ECMAScript 原生 type 定義。
Number
則為 ECMAScript 型別,可根據使用到 type 自行引入。
第 3 行
let def = create({ checkTypes: true, env })
由 create()
建立 def()
。
第 5 行
let add = def('add')({})([Number, Number, Number]) (
x => y => x + y
)
使用 def()
定義 add()
。
- 第一個 argument 定義 function name
- 第二個 argument 定義 typeclass constraint,若沒使用傳入 empty object 即可
- 第三個 argument 定義 type signature
- 第四個 argument 定義實際 function
如此自行定義的 function 也能受到 Sanctuary 的 Type Checking 保護。
Conclusion
- Sanctuary 的 type checking 解決了 TypeScript 很難使用 curry function 的老問題,天生就是為了 Hindley-Milner type system 設計,而且也支援 typeclass constraint,可以說是 ECMAScript 在 Functional Programming 下最好解決方案