2013-11-21 80 views
2

我一直在試圖理解這段代碼,但我不能夠清楚地包起來:

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]在上述類型簽名發起了?

回答

6

爲了zipWith ($)到類型檢查,我們必須統一zipWith類型與($)類型的第一個參數。我會把它們一起寫出來,並用獨特的名字來使它更清楚。

zipWith :: (a  -> b -> c) -> [a]  -> [b] -> [c] 
($)  :: ((x -> y) -> x -> y) 

因此,zipWith typechecks當且僅當我們可以假設a ~ (x -> y)b ~ xc ~ y。沒有什麼能夠阻止這種成功的統一,所以我們可以用zipWith這些名字替換這些名字。

zipWith :: ((x -> y) -> x -> y) -> [x -> y] -> [x] -> [y] 
($)  :: ((x -> y) -> x -> y) 

然後與應用程序進行,因爲一切很好地匹配起來現在

zipWith ($) :: [x -> y] -> [x] -> [y] 

這相當於高達類型的變量名與你看到的類型的具體選擇。

2

這只是上下文替換,並沒有魔法。看:

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語義:採取拉鍊(第一個參數),然後將兩個列表壓縮在一起。如果您的拉鍊是功能應用程序($是函數應用程序,是!),那麼當壓縮兩個列表時,您只需使用第二個列表的相應元素調用第一個列表的元素。而功能類型反映了這一點。

0

在類型簽名中分配的實際字母是任意的,可以是任何東西。你可以很容易地編寫($)的類型,

(x -> y) -> x -> y 

它有兩個參數,一個函數取一個參數,和值傳遞到函數。

zipWith的第一個參數是一個帶有兩個參數(a -> b -> c)的函數。鑑於($)的定義,你選擇a作爲(x -> y)b作爲x然後cy,所以你得到的zipWith ($)類型爲

[x -> y] -> [x] -> [y] 
0

讓我們改寫了一下我們的簽名:

($) :: (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] 
+0

你可以使用其中的語法W /類型簽名? –

+0

@dave謝謝!當然,這是不可能的 – wit

+0

我以爲你正在使用一些很酷的ghc擴展或某些東西 –