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