我一直在試圖理解這段代碼,但我不能夠清楚地包起來:
ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a -> b) -> a -> b
ghci > let c = zipWith ($)
ghci > :t c
c :: [b -> c] -> [b] -> [c]
如何[b -> c]
在上述類型簽名發起了?
我一直在試圖理解這段代碼,但我不能夠清楚地包起來:
ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a -> b) -> a -> b
ghci > let c = zipWith ($)
ghci > :t c
c :: [b -> c] -> [b] -> [c]
如何[b -> c]
在上述類型簽名發起了?
爲了zipWith ($)
到類型檢查,我們必須統一的zipWith
類型與($)
類型的第一個參數。我會把它們一起寫出來,並用獨特的名字來使它更清楚。
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
($) :: ((x -> y) -> x -> y)
因此,zipWith
typechecks當且僅當我們可以假設a ~ (x -> y)
,b ~ x
和c ~ y
。沒有什麼能夠阻止這種成功的統一,所以我們可以用zipWith
這些名字替換這些名字。
zipWith :: ((x -> y) -> x -> y) -> [x -> y] -> [x] -> [y]
($) :: ((x -> y) -> x -> y)
然後與應用程序進行,因爲一切很好地匹配起來現在
zipWith ($) :: [x -> y] -> [x] -> [y]
這相當於高達類型的變量名與你看到的類型的具體選擇。
這只是上下文替換,並沒有魔法。看:
ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a' -> b') -> a' -> b'
現在考慮zipWith ($)
。它的類型爲(a -> b -> c) -> [a] -> [b] -> [c]
,其中第一個參數是固定的,所以我們應該與(a' -> b') -> a' -> b'
($
的類型)匹配(a -> b -> c)
(第一個參數的類型)。因此我們有a = (a' -> b')
,b = a'
,c = b'
。替補回到zipWith
:[a' -> b'] -> [a'] -> [b']
(第一個參數是固定的,所以他從類型中消失),這正是你用類型變量命名的不同。
另外,人們可能會考慮zipWith
語義:採取拉鍊(第一個參數),然後將兩個列表壓縮在一起。如果您的拉鍊是功能應用程序($
是函數應用程序,是!),那麼當壓縮兩個列表時,您只需使用第二個列表的相應元素調用第一個列表的元素。而功能類型反映了這一點。
在類型簽名中分配的實際字母是任意的,可以是任何東西。你可以很容易地編寫($)的類型,
(x -> y) -> x -> y
它有兩個參數,一個函數取一個參數,和值傳遞到函數。
zipWith
的第一個參數是一個帶有兩個參數(a -> b -> c)
的函數。鑑於($)
的定義,你選擇a
作爲(x -> y)
和b
作爲x
然後c
是y
,所以你得到的zipWith ($)
類型爲
[x -> y] -> [x] -> [y]
讓我們改寫了一下我們的簽名:
($) :: (a -> b) -> a -> b
($) :: a' -> b' -> c'
where -- pseudo-Haskell
a' = a -> b
b' = a
c' = b
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
而且發現,這
zipWith ($) :: [a'] -> [b'] -> [c']
zipWith ($) :: [a -> b] -> [a] -> [b]
你可以使用其中的語法W /類型簽名? –
@dave謝謝!當然,這是不可能的 – wit
我以爲你正在使用一些很酷的ghc擴展或某些東西 –