关于Haskell中的构图的困惑

时间:2018-12-11 13:38:17

标签: haskell

我正在研究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

我看不到结果类型签名是如何发生的。如果给我该函数并要求编写类型签名,那我将无可救药。

以下内容也一样。在“遍历”一章中,我们被告知traverse仅由sequenceAfmap组成:

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的类型签名?

在这里超级迷路了,任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:7)

也许只是在视觉上对齐类型会使您对管道的进展方式有所了解,并帮助您迈向下一个混乱点!

display:block

要在屏幕上保留下一个,我们将(==) :: Eq a => a -> (a -> Bool) any :: (a -> Bool) -> (t a -> Bool) any . (==) :: Eq a => a -> (t a -> Bool) 缩写为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) 为何对fmap g的约束是t而不是Traversable?好吧,没问题:我们实际上可以给它更宽松的条件类型Functor。但是由于每个fmap g :: Functor t => ...必须是一个Traversable,所以也可以指定这种类型,这样可以使并行性更加清晰。)

答案 1 :(得分:3)

All Haskell functions take just one argument-甚至我们经常认为带有多个参数的那些。考虑您的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

(==)的类型可以读为(==) :: Eq a => a -> (a -> Bool):它需要一个a值(a可以是Eq的任何实例)并提供一个a -> Bool函数。 any依次接受a -> Bool函数(a可以是任何东西)并赋予t a -> Bool函数(t可以是作为...的实例的任何东西Foldable)。就是这样,any . (==)管道中的中间类型是Eq a => a -> Bool