问题描述
简化this问题。凭直觉,我已经了解了sequenceA
在该用例中的作用,但 如何/为什么 那样工作。
因此,所有问题归结为一个问题: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
来自哪里?我的意思是,我知道这是所有对中++
的串联,但是我不知道它是通过{{1}列表中的concat
还是通过fst
} s。 sequenceA
的签名中似乎没有任何东西可以强制要求成对的fst
可以组合在一起。
仍然必须在某个地方使用该假设。确实,以下失败
sequenceA [('a',('b',('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
的{{1}}实例。
这些必需的约束出现在实例的Haddock中的Monoid
之前,如下所示:
=>
为什么需要Monoid a => Applicative ((,) a)
实例?例如,Monoid
的{{1}}需要凭空实现一个pure
值,以将其放入元组的第一个元素。类型为((,) a)
的{{1}}可以完成工作。
可能存在许多层次的所需约束链。例如,为什么以下方法起作用?
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)
。
实际上,还有一个{em> Monoid
实例,这些实例可以通过Endo
新类型使用。尽管需要一定数量的包装和展开,但通常使用newtypes选择用于给定操作的实例。