我正在研究Haskell的书,我意识到我很难理解函数组合.在一个非常基本的层面上,我有一个管道的心理模型,它接受一个输入并将其结果传递给合成中的下一个函数.对于简单的功能,这很容易.
我遇到困难的地方是理解组成函数的结果类型签名是如何形成的.例如,如果我们看一下基本定义elem:
elem :: (Foldable t, Eq a) => a -> t a -> Bool
elem = any . (==)
>:t (==)
(==) :: Eq a => a -> a -> Bool
>:t any
any :: Foldable t => (a -> Bool) -> t a -> Bool
Run Code Online (Sandbox Code Playgroud)
我无法看到生成的类型签名是如何发生的.如果我被赋予了这个功能并要求写出类型签名,那么我将无可救药地丢失.
以下情况也是如此.在Traversable一章中,我们被告知这traverse是公正sequenceA和fmap有组织的:
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
>:t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
>:t sequenceA
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
Run Code Online (Sandbox Code Playgroud)
我自己了解每个函数类型签名,但它们如何结合创建traverse类型签名?
超级迷失在这里,任何帮助将不胜感激.
也许只是在视觉上对齐类型将使您获得关于管道如何进展的一些直觉的一部分,并帮助您在下一个混乱点上取得进展!
(==) :: Eq a => a -> (a -> Bool)
any :: (a -> Bool) -> (t a -> Bool)
any . (==) :: Eq a => a -> (t a -> Bool)
Run Code Online (Sandbox Code Playgroud)
为了让一个屏幕上的下一个,让我们的缩写Traversable来T和Applicative到A.你的问题中也有两个f,一个在计算级别,一个在类型级别.为了避免混淆,我要你的计算级别的命名f来g代替.所以如果g :: a -> f b对某些人Applicative f:
fmap g :: T t => t a -> t (f b)
sequenceA :: (T t, A f) => t (f b) -> f (t b)
sequenceA . fmap g :: (T t, A f) => t a -> f (t b)
\g -> sequenceA . fmap g :: (T t, A f) => (a -> f b) -> t a -> f (t b)
Run Code Online (Sandbox Code Playgroud)
(等待怎么来!fmap g,约束的t是Traversable,不Functor好吧,没问题:我们实际上可以给它更宽松的类型fmap g :: Functor t => ...,但因为每一个.Traversable必须是一个Functor,也可以给出这种类型,这使得相似之处更加清晰. )