我正在从Hutton的《 Haskell编程》中阅读有关Haskell的Applicative
的信息。为了更好地理解它,我为列表的Applicative
提出了以下定义:
-- Named as pure' and "app" to avoid confusion with builtin versions
class Applicative' f where
pure' :: a -> f a
app :: f (a->b) -> f a -> f b
instance Applicative' [] where
pure' x = [x]
app _ [] = []
app [g] (x:xs) = [(g x)] ++ app [g] xs
app (g:gs) (x:xs) = [(g x)] ++ app gs xs
-- fmap functions could be defined as:
fmap1' :: (Applicative' f)=>(a->b) -> f a -> f b
fmap1' g x = app (pure' g) x
fmap2' :: (Applicative' f)=>(a->b->c) -> f a -> f b -> f c
fmap2' g x y = app (app (pure' g) x) y
fmap3' :: (Applicative' f)=>(a->b->c->d) -> f a -> f b -> f c -> f d
fmap3' g x y z = app (app (app (pure' g) x) y) z
fmap2'
的使用示例如下:Ok, one module loaded.
*Main> g = \x y -> x*y
*Main> arr1 = [1,2,3]
*Main> arr2 = [4,5,6]
*Main> fmap2' g arr1 arr2
[4,10,18]
*Main>
但是列表的
Applicative
函数<*>
的标准定义定义为:gs <*> xs = [g x | g <- gs, x <- xs]
因此导致
pure (*) <*> [1,2], [3,4]
[3,4,6,8]
我想知道为什么它以
for all arr1, for all arr2, apply function
而不是take corresponding elements arr1, arr2 apply function
的方式定义。我想第一个定义可能更有用?选择此选项有什么具体原因吗?
最佳答案
Applicative []
具有generate-all-possible-combinations行为而不是任何一种zippy行为的基本原因是,Applicative
是Monad
的超类,并且旨在根据Monad
实例的存在进行操作。 Monad []
将列表视为失败和优先选择,因此Applicative []
实例也是如此。人们经常使用应用接口重构单子代码,以减少值所需的中间名的数量,并增加并行的机会。如果这导致功能语义上的重大转变,那将是非常可怕的。
除此之外,事实是,您对于Applicative []
实例的选择非常满意,如果考虑空/非空和有限/协和/无限变化,则更是如此。这是为什么?
就像我在this answer中提到的那样,在我们开始担心值之前,每个Applicative f
都以Monoid (f ())
开头,结合了数据的形状。列表就是一个很好的例子。[()]
基本上是数字的类型。在很多方面,数字都是类人动物。
从Applicative []
中获取Monad []
等于选择1
和*
生成的monoid。
同时,Applicative ZipList
利用了Haskell的协合混合,相当于选择了由无穷大和极小值生成的类半群。
该问题提出了一个不合法的实例,但接近一个实例。您会注意到<*>
没有为空的函数列表定义,但是对于非空的函数列表,它会填充以匹配参数列表。当参数用完时,它会不对称地截断。不太正确。
随后有两个候选修复程序。
一种是在两边都空截断,然后必须使用pure = repeat
,并且您具有ZipList
。
另一种是排除空白列表并在两边填充。然后,您将从Applicative
生成的Monoid
获得1和最大值所生成的正数。因此,它根本不是ZipList
。那就是我在this answer中称为PadMe
的东西。您需要排除0的原因是,对于<*>
输出中的每个位置,您都需要指向函数及其参数分别来自的两个输入中的位置。如果您一无所有,则无法进行填充。
这是一个有趣的游戏。在数字上选择一个Monoid
,看看是否可以将其扩展为列表的Applicative
!