sequenceA 如何处理成对列表?
How does sequenceA work on lists of pairs?
从 问题中分离出来。直觉上我已经理解 sequenceA
在那个用例中做了什么,但不是 how/why 它是那样工作的。
所以这一切都归结为这个问题:sequenceA
在以下情况下如何工作?
> sequenceA [("a",1),("b",2),("c",3)]
("abc",[1,2,3])
我明白了
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
所以在上面的用例中 Traversable
是 []
,而 Applicative
,因为 (,)
是二进制类型构造函数,所以是 (,) a
,这意味着该对在其 snd
字段上被视为应用函子。这与结果的 snd
中的列表一起出现。所以我们从一个对列表转到第二个字段中有一个列表的对。
但是 "abc"
是从哪里来的呢?我的意思是,我知道它是所有对的 fst
的串联,但我不知道它是通过 ++
还是通过 [=23] 列表的 concat
=]秒。 sequenceA
的签名中似乎没有任何内容来强制 fst
对可以组合在一起。
仍然必须在某处使用该假设。事实上,以下失败
sequenceA [('a',1),('b',2),('c',3)]
它使用mappend
。它使用的 Applicative
实例如下所示:
instance Monoid a => Applicative ((,) a) where
pure x = (mempty, x)
(af, f) <*> (ax, x) = (mappend af ax, f x)
在 Haskell 中,一个类型的类型类实例可以以该类型的某些部分的其他类型类实例的存在为“条件”。不是 all 类型的构造函数
形式 ((,) a)
是 Applicative
的实例,但 仅 那些 a
类型具有 Monoid
实例的实例。
这些必需的约束出现在实例的 Haddocks 中的 =>
之前,如下所示:
Monoid a => Applicative ((,) a)
为什么需要 Monoid
实例?首先,pure
for ((,) a)
需要凭空具体化一个 a
值以放入元组的第一个元素。 mempty
类型 a
完成工作。
可以有多个级别深的所需约束链。例如,以下为什么有效?
ghci> import Datta.Function ((&)) -- flipped application
ghci> [(id :: String -> String, 2 :: Int), (\x -> x ++ x, 1)] & sequenceA & fst $ "foo"
"foofoofoo"
这里的第一个组件是一个函数。和以前一样,它必须有一个 Monoid
实例才能使 sequenceA
起作用。但是什么时候类型 a -> b
是 Monoid
?查看黑线鳕,我们发现:
Monoid b => Monoid (a -> b)
也就是说,当 return 类型(此处 String
)是 Monoid
时,函数是 Monoid
。
实际上,另一个 Monoid
函数实例可通过 Endo
新类型使用。通常使用新类型 select 哪个实例用于给定操作,尽管它需要一定量的包装和解包。
从 sequenceA
在那个用例中做了什么,但不是 how/why 它是那样工作的。
所以这一切都归结为这个问题:sequenceA
在以下情况下如何工作?
> sequenceA [("a",1),("b",2),("c",3)]
("abc",[1,2,3])
我明白了
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
所以在上面的用例中 Traversable
是 []
,而 Applicative
,因为 (,)
是二进制类型构造函数,所以是 (,) a
,这意味着该对在其 snd
字段上被视为应用函子。这与结果的 snd
中的列表一起出现。所以我们从一个对列表转到第二个字段中有一个列表的对。
但是 "abc"
是从哪里来的呢?我的意思是,我知道它是所有对的 fst
的串联,但我不知道它是通过 ++
还是通过 [=23] 列表的 concat
=]秒。 sequenceA
的签名中似乎没有任何内容来强制 fst
对可以组合在一起。
仍然必须在某处使用该假设。事实上,以下失败
sequenceA [('a',1),('b',2),('c',3)]
它使用mappend
。它使用的 Applicative
实例如下所示:
instance Monoid a => Applicative ((,) a) where
pure x = (mempty, x)
(af, f) <*> (ax, x) = (mappend af ax, f x)
在 Haskell 中,一个类型的类型类实例可以以该类型的某些部分的其他类型类实例的存在为“条件”。不是 all 类型的构造函数
形式 ((,) a)
是 Applicative
的实例,但 仅 那些 a
类型具有 Monoid
实例的实例。
这些必需的约束出现在实例的 Haddocks 中的 =>
之前,如下所示:
Monoid a => Applicative ((,) a)
为什么需要 Monoid
实例?首先,pure
for ((,) a)
需要凭空具体化一个 a
值以放入元组的第一个元素。 mempty
类型 a
完成工作。
可以有多个级别深的所需约束链。例如,以下为什么有效?
ghci> import Datta.Function ((&)) -- flipped application
ghci> [(id :: String -> String, 2 :: Int), (\x -> x ++ x, 1)] & sequenceA & fst $ "foo"
"foofoofoo"
这里的第一个组件是一个函数。和以前一样,它必须有一个 Monoid
实例才能使 sequenceA
起作用。但是什么时候类型 a -> b
是 Monoid
?查看黑线鳕,我们发现:
Monoid b => Monoid (a -> b)
也就是说,当 return 类型(此处 String
)是 Monoid
时,函数是 Monoid
。
实际上,另一个 Monoid
函数实例可通过 Endo
新类型使用。通常使用新类型 select 哪个实例用于给定操作,尽管它需要一定量的包装和解包。