我試圖將這個JS定點運算子翻譯成Haskell.
。JS:
const fix = F => {
const D = X => F(
t => X(X)(t
)
return D(D)
};
我的嘗試是(Haskell):
fix'/span> f = d d
where
d x = f ( -> x x t)
然而,我得到了以下錯誤:
Couldn't match expected type ' (t2 -> t3) ->/span> t4'
與實際型別'p'
因為type variables 't2', 't3', 't4'會逃脫它們的范圍
這些(剛性的。skolem)型別 變數被推斷的型別 of d所系結。 : (t1 -> t2 -> t3) -> /span> t4
有人知道這里發生了什么嗎?
uj5u.com熱心網友回復:
在自我應用d d中,d既是一個函式,其型別為a -> r,又是其引數,其型別為a。因此這兩種型別必須是相同的,a ~ (a -> r)。
Haskell想要預先知道它的全部型別,所以它不斷地用一個型別替換另一個型別,最終得到一個無限的型別。
Haskell中不允許有無限型別,但允許有遞回型別。
我們在這里所要做的就是為這個遞回型別命名:
newtype T r = D { app : : T r -> r }
現在T r既是一個函式的型別,也是它的引數,對于某個結果型別r。
這里T是一個型別建構式,而D是其資料建構式,D :: (T r -> r) -> T r。
上面的記錄語法定義了一個新的資料型別(這里用的是關鍵字newtype,而不是data)并將其單一欄位命名為app。它還定義了app作為一個訪問函式,app :: T r -> (T r -> r)。(它有點像D的倒數,而且經常看到這樣的函式以 "un "為前綴命名,比如app可以被命名為unD。但在這里app是有意義的,我們將在后面看到。)
對于一個型別為T r的值x,x :: T r,這意味著x是/與/某個值相匹配的D g,其中(g = app x) :: T r -> r,即app簡單地解開了資料建構式D,從而得到了底層的值(一個函式)g。x = D g; app x = app (D g) = g。這就是記錄語法在Haskell中的作用。
現在我們可以寫
{- fix' f= d d
其中
d x = f ( -> x x t) -- 將x應用于x不能被輸入!
-}
fix1 :: ((t1 -> t) -> t1 -> t) -> t1 -> t
fix1 f = d (D d)
where
d x = f ( -> app x x t) -- `app`ing x to x is well-typed!
fix2 :: ((t1 -> t) -> t1 -> t) -> t1 -> t
fix2 f = d (D d)
where
d (D y) = f ( -> y ( D y) t)
fix3 :: ((t1 -> t) -> t1 -> t) -> t1 -> t
fix3 f = f ( -> d ( D d) t)
where
d (D y) = f ( -> y (D y) t)
fix4 :: (t -> t) -> t
fix4 f = f (d (D d)
where
d (D y) = f (y (D y)
所有的作業。最后一個甚至與內置的fix的型別相同。
但是Haskell不僅有遞回型別。它也有遞回本身。一個物體是允許在它自己的定義中參考自己。
因此,正如評論中所說,我們并不真的需要通過對作為引數傳遞的值進行自我應用來模擬遞回。我們只需要遞回地使用被定義的函式本身即可:
fix0 :: (t -> t) -> t
fix0 f = f (fix0 f)
或者我們可以使用一個遞回定義的值:
y :: (t -> t) -> t
y f = x where { x = f x }
關于錯誤,第二個型別錯誤你得到,
prog.hs:3:22:錯誤。
- Occurs檢查:無法構建無限的型別:。
t1 ~ t1 -> t2 -> t3
- 在第一個引數的'x',即'x'
在運算式: x x t
在第一個引數的'f',即'( t -> x x t)'
- 相關的系結包括
t :: t2 (系結在 prog.hs:3:15)
x :: t1 -> t2 -> t3 (在prog.hs:3:7的范圍內)
d :: (t1 -> t2 -> t3) -> t4 (bound at prog.hs:3:5)
|
3 | d x = f ( -> x x t)
| ^
似乎比你包含的那個更有針對性/更有幫助。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/316945.html
標籤:
上一篇:如何用Shake來分配PTY?
