2014-10-08 84 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