2014-10-08 82 views
2

我相信這是非常明顯的,但忍耐我,我是新來的這個東西,它不是點擊。 所以我和其他許多人一樣,一直試圖讓我的頭在Monads身邊。我已經到了適合>> =和返回操作員等等的地步。但我覺得我不會真正理解它,直到我落後它並自己寫作。按類型指定函數參數

因此,我一直在試圖實現List >>的bind >> =操作符作爲map和foldr的組合。例如,[5,6,7,8] >>= (\x -> [x*5, x*6, x*7])產生[25,30,35,30,36,42,35,42,49,40,48,56]。這看起來很像地圖和摺疊的組合。但是,如果我嘗試類似foldr (++) [] . map的東西,我會得到一個明顯的類型錯誤,即地圖沒有按照預期的類型[a] -> [[a]]。當然,如果我使用諸如map (\x -> [x*5, x*6, x*7])之類的東西作爲合成操作符的正確參數,它都可以工作。

但是,每次指定一個特定的函數會很麻煩;不知何故,>> =運算符以更一般的方式運行。有沒有辦法通過它的類型指定參數?像,我可以不知何故告訴地圖在這個組合中只採用a -> [a]類型的函數嗎?我是否需要編寫一個類型爲(a -> [a]) -> [a] -> [[a]]的函數,因爲沒有辦法將地圖函數縮小爲我想要的函數類型?

另外,請隨時告訴我,我接近這一切都是錯誤的。我對這種類型的東西仍然很陌生。如果是這樣,請指出我正確的方向。

回答

7

如果選中的像

> :t \f -> foldr (++) [] . map f 

類型的GHCI像我一樣上面,你會發現一些有趣的事情

\f -> foldr (++) [] . map f :: (a -> [b]) -> [a] -> [b] 

或者,要切入正題,事實證明,輸入功能f已經自然有你要求的更多限制類型。爲什麼會這樣?

讓我們來看看其中foldr (++) []更自然地稱爲concat

concat :: [[a]] -> [a] 
concat = foldr (++) [] 

我們可以看到,它的輸入必須是列表的列表中。如果我們考慮一下,在後組成的背景下意味着map

concat   ::  [[c]] -> [c] 
     . map f :: [a] -> [ b ]   -- for (f :: a -> b) 

我們可以看到,b必須是相同的[c]某種類型c。換句話說,關於使用映射結果f的信息,例如,其通過concat,有倒流專門瞭解我們所知道的有關map甚至其參數f的信息。

所以,統一b[c]上面我們可以看到,map必須有一個稍微更嚴格的類型

map ::* (a -> [c]) -> [a] -> [[c]] 

,我寫(::*)以表明這是map的自然類型的專業化進行自然由統一與類型concat

+1

該死的,哈斯克爾聰明;比我更聰明。這很奇怪。謝謝,答案接受。這太棒了。 – 2014-10-08 03:10:20