我确定这很明显,但请耐心等待,我对这些东西不熟悉并且没有点击。 所以我和许多其他人一样,试图让我的头围绕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])
之类的东西作为合成运算符的正确参数,那么它一切正常。
但每次指定一个特定的功能都会很麻烦;不知何故,>> =运算符以更一般的方式运行。有没有办法按类型指定参数?比如,我可以以某种方式告诉map只在这个组合中使用a -> [a]
类型的函数吗?我是否需要从头开始编写一个类型为(a -> [a]) -> [a] -> [[a]]
的函数,因为实际上没有办法将map函数缩小到我想要的函数类型?
另外,请随时告诉我,我接近这一切都是错的。我对这类东西还很陌生。如果是这样,请指出我正确的方向。
答案 0 :(得分: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
。