所以我希望fmap
一个函数超过几个相同大小的Vector
。好吧,Applicative
救援!
λ➔ let f x y z = x * y + z
λ➔
λ➔ let va = V.fromList [10, 20, 30]
λ➔ let vb = V.fromList [2, 5, 3]
λ➔ let vc = V.fromList [1, 2, 3]
λ➔
λ➔ f <$> va <*> vb <*> vc
fromList [21,22,23,51,52,53,31,32,33,41,42,43,101,102,103,61,62,63,61,62,63,151,152,153,91,92,93]
...哎呀。远非预期。
从the source开始,显然Applicative实例是根据Monad的定义; (<*>) = ap
; (>>=) = flip concatMap
。这解释了为什么我的表情爆炸了:
λ➔ (+) <$> V.fromList [10, 20, 30] <*> V.fromList [1, 2, 3]
fromList [11,12,13,21,22,23,31,32,33]
它只列举所有参数组合。这似乎是(<*>)
的一个非常糟糕的定义,因为结果是组合起来的。
相反,它可以对角化结果以生成[11, 22, 33]
,如下所示:
instance Applicative Vector where
pure = repeat
fs <*> xs = zipWith ($) fs xs
这仍然是一个有效的Applicative
实例。在我看来,它更有用。
除了明显的疏忽之外,是否有理由按照原样编写实例?
此外,对于采取进一步行动的经验丰富的建议表示赞赏。
*编辑:更早更正pure = single
答案 0 :(得分:11)
这不仅仅是疏忽。为了使Applicative
实例正确,我们需要pure
的实现,其行为类似于无限长向量,所有实例都具有相同的元素。 (并且因为API提供扫描,所以库也需要能够具有类似于任意无限长向量的值。)当前实现使用数组是出于性能原因;由于显而易见的原因,人们无法拥有无限大小的阵列。
在zippy应用程序变得流行之前,您可以按照我们以前的方式实现您想要的目标。有一些函数专门用于你想要压缩的参数数量,通常有zipWithN
个名字。
答案 1 :(得分:5)
我只会使用Vector.zipWith
。
我从使用Vector
中学到的一件事是,您通常希望坚持图书馆的操作并避开Functor
/ Applicative
/ Monad
个实例。为什么?因为几个库的类型不能支持类型类。例如,如果您使用Data.Vector.Vector
的{{1}}实例编写了大量代码,并且稍后决定使用Functor
,那么您将不得不更改所有Data.Vector.Unboxed.Vector
}到fmap
,因为未装箱的向量不是Data.Vector.Unboxed.map
个实例 - 元素类型带有Functor
约束。
这很不幸,但它只是一个长期未解决的Haskell问题的例子(当类型需要对类型参数进行约束时,无法生成Unbox
类的实例。)
答案 2 :(得分:0)
您可以输入一个类型:
数据DiagonalisingVector a = DiagVector(a a(向量a))
为此编写适用的实例很容易转发Right的所有内容,并向左应用Right的每个元素或另一个单数Left。左表示纯值,可以认为它占据所有点。
这将很容易地做你想要的,你可以开始根据自己的需要定制和优化它。