我正在阅读 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
我看不到生成的类型签名是如何发生的。如果给我这个函数并要求我编写类型签名,我会绝望地迷失方向。
下面的情况也是如此。在 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)
我自己理解每个函数的类型签名,但它们如何结合创建
traverse
的类型签名?在这里 super 迷路,任何帮助将不胜感激。
最佳答案
也许仅仅在视觉上对齐类型会让你对管道的进展有一些直觉,并帮助你朝着下一个困惑点前进!
(==) :: Eq a => a -> (a -> Bool)
any :: (a -> Bool) -> (t a -> Bool)
any . (==) :: Eq a => a -> (t a -> Bool)
要在一个屏幕上保留下一个,我们缩写
Traversable
至T
和 Applicative
至A
.您还有两个 f
s 在您的问题中,一个在计算级别,一个在类型级别。为避免混淆,我将重命名您的计算级别 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)
(等等!为什么
fmap g
,t
的约束是 Traversable
而不是 Functor
?好吧,没问题:我们实际上可以给它更宽松的类型 fmap g :: Functor t => ...
。但是由于每个 Traversable
必须是 Functor
,也可以使用这种类型,这样可以使相似之处更加清晰。)
关于haskell - 关于 Haskell 中的组合的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53725329/