PureScript與JavaScript中equality設(shè)計(jì)的使用對(duì)比分析
引言
從 PureScript 的角度反過(guò)來(lái)看, JavaScript 的好多概念還是比較模糊的.
前幾天群里看到討論 js 的 equality 的事情, 我就覺得 js 設(shè)計(jì)挺不清晰的.
js 里用 ===
的話, 遇到
- literals 和 null/undefined, 按照 value 進(jìn)行判斷(我不懂 NaN...)
- Array, Object, 按照引用進(jìn)行判斷
但是作為 calcit-js 作者, 我想說(shuō), 這些不是數(shù)據(jù)本身 equality 的性質(zhì),解釋器當(dāng)中的語(yǔ)義, 完全是編程語(yǔ)言作者設(shè)計(jì)了暴露出來(lái)的接口,判斷引用來(lái)判斷是否相等, 運(yùn)行指令的時(shí)候會(huì)非常快, 但這不是我們業(yè)務(wù)當(dāng)中想要的語(yǔ)義.
我們?cè)跇I(yè)務(wù)當(dāng)中遇到數(shù)據(jù)要判斷是否"等價(jià)", 就需要按照值進(jìn)行判斷,當(dāng)我們用組合而成的數(shù)據(jù)結(jié)構(gòu)來(lái)表示數(shù)據(jù)的時(shí)候, 就需要根據(jù)結(jié)構(gòu)遞歸進(jìn)行判斷.
而 js 的 ===
只是一個(gè)封裝暴露平臺(tái)底層能力的功能, 沒有往函數(shù)式編程做.
PureScript 當(dāng)中怎么判斷是否相等的
開始之前, 需要定義一個(gè)數(shù)據(jù)類型, 比如我直接叫做 Cirru
,
data Cirru = CirruString String | CirruList (Array String)
這段代碼的含義是我定一個(gè)了一個(gè)類型 Cirru, 有兩種情況,
- 一個(gè)是通過(guò)數(shù)據(jù)構(gòu)造器
CirruString "a"
構(gòu)造的第一種可能, - 一個(gè)是通過(guò)數(shù)據(jù)構(gòu)造器
CirruList ["b"]
構(gòu)造的第二種可能,
用兩個(gè)簡(jiǎn)單的數(shù)據(jù)進(jìn)行判斷
比方說(shuō)我們用兩個(gè)簡(jiǎn)單的數(shù)據(jù)進(jìn)行判斷的時(shí)候,
(CirruString "A") == (CirruString "B")
PureScript 類型檢查會(huì)報(bào)錯(cuò), 認(rèn)為找不到 Cirru 這個(gè)類型怎么處理的辦法,
No type class instance was found for Data.Eq.Eq Cirru while applying a function eq of type Eq t0 => t0 -> t0 -> Boolean to argument CirruString "A" while inferring the type of eq (CirruString "A") in value declaration main where t0 is an unknown type
因?yàn)槭呛瘮?shù)式編程, 它的內(nèi)部其實(shí)會(huì)把 ==
轉(zhuǎn)換回到一個(gè)函數(shù) eq
,
eq (CirruString "A") (CirruString "B")
通過(guò)函數(shù)進(jìn)行判斷, 但是 eq
在 type class 當(dāng)中只是通用的定義,
eq :: a -> a -> Boolean
https://pursuit.purescript.or...
其中 a
是某個(gè)類型, 但他并不知道 Cirru 對(duì)應(yīng)的 eq
, 就得到了上邊的報(bào)錯(cuò),
No type class instance was found for Data.Eq.Eq Cirru while applying a function eq of type Eq t0 => t0 -> t0 -> Boolean to argument CirruString "A" while inferring the type of eq (CirruString "A") in value declaration main where t0 is an unknown type
PureScript derive 集成內(nèi)置的實(shí)現(xiàn)
作為 js 程序員, 當(dāng)然你也會(huì)有一些常見的處理辦法, "直接遞歸判斷不就好了?",那么 PureScript 也提供了這樣的一種方式, 你可以通過(guò) derive
集成內(nèi)置的一個(gè)實(shí)現(xiàn),
derive instance cirruEq :: Eq Cirru
這樣當(dāng)你再去判斷的時(shí)候, 就能得到期望的結(jié)果了,
main = do log $ show $ (CirruString "A") == (CirruString "B") log $ show $ eq (CirruString "A") (CirruString "B") log $ show $ (CirruString "A") == (CirruString "A") log $ show $ eq (CirruString "A") (CirruString "A") log $ show $ (CirruList ["A"]) == (CirruList ["A"])
=>> spago run
[info] Build succeeded.
false
false
true
true
true
或者, 另一種更常見的方式是你自己去定義 eq
對(duì)應(yīng)對(duì)的實(shí)現(xiàn)是什么樣?
比如這樣的代碼, 就通過(guò)遞歸, 把判斷變成內(nèi)部的數(shù)據(jù)的判斷,
instance cirruEq :: Eq Cirru where eq (CirruString x) (CirruString y) = x == y eq (CirruList x) (CirruList y) = x == y eq _ _ = false
總結(jié)
最后 eq _ _
表示剩下的其他的沒覆蓋到的可能性.
從這個(gè)代碼的原理上說(shuō), 你完全可以自己定義數(shù)據(jù)是否相等的規(guī)則,即便你把 (CirruString "A") == (CirruList ["A"])
定義相等, 也能寫出來(lái)
instance cirruEq :: Eq Cirru where eq (CirruString x) (CirruString y) = x == y eq (CirruList x) (CirruList y) = x == y -- 看這行 eq (CirruString x) (CirruList ys) = if (length ys) == 1 then (Just x) == (head ys) else false eq _ _ = false
你可以簡(jiǎn)單認(rèn)為是 type class 先定義出來(lái)了一套接口, 其中有 eq
,然后其他類型定義好后, 也需要定義對(duì)應(yīng)的 eq
的實(shí)現(xiàn), 然后才能用于計(jì)算.
JavaScript 作為腳本語(yǔ)言, 把其中一種特例, 硬編碼在語(yǔ)言解釋器上了.
你要找準(zhǔn)確的理解, 就要回到 PureScript 這樣的定義當(dāng)中去.
可以看到, 在語(yǔ)義層面我們不會(huì)去觸碰"內(nèi)存引用的地址是否相等"的判斷,當(dāng)你在 PureScript 用 FFI 的方式去調(diào)用 js, 可能是會(huì)碰到,但是這些硬件相關(guān)的實(shí)現(xiàn)細(xì)節(jié), 是盡量被封裝到語(yǔ)言實(shí)現(xiàn)的底層當(dāng)中去隱藏起來(lái)的.
本文是我的一些學(xué)習(xí)筆記和新得, 使用的術(shù)語(yǔ)不大準(zhǔn)確,如果你需要更精確的概念, 可能需要翻一下 Haskell 相關(guān)文檔,更多關(guān)于PureScript equality對(duì)比JavaScript 的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
TypeScript使用strictnullcheck實(shí)戰(zhàn)解析
這篇文章主要為大家介紹了TypeScript使用strictnullcheck實(shí)戰(zhàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08TypeScript?泛型接口具體使用實(shí)戰(zhàn)
這篇文章主要為大家介紹了TypeScript?泛型接口具體使用實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Typescript編碼規(guī)范ESLint和Prettier使用示例詳解
這篇文章主要介紹了Typescript編碼規(guī)范ESLint和Prettier使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09TypeScript之Generics泛型類型學(xué)習(xí)
這篇文章主要為大家介紹了TypeScript之Generics泛型類型學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07