为什么列表Applicative实例不进行一对一的应用呢?

Why doesn't the list Applicative instance perform a one-to-one application?

我在 Haskell 中阅读了 Haskell 中关于 Applicative 的 Hutton's Programming in Haskell。为了更好地理解它,我为列表 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。 我想第一个定义可能更有用?这个选择有什么具体原因吗?

这基本上就是 ZipList 应用实例。主要区别是

pure x = repeat x

而不是你的pure x = [x]

这是满足适用法律所必需的。也就是说,您的实施违反了交换法则:

[id, id] <*> pure 1 ≡ [id,id] <*> [1]
                    ≡ [id 1] ++ ([id] <*> [])
                    ≡ [id 1] ++ []
                    ≡ [1]
‡ pure ($ 1) <*> [id,id] ≡ [1,1]

无限 pure 的要求使得 ZipList 在实践中有些滑稽。标准实现基本上是最自然的 finite-only 实现。可以说,如果有限数组和前奏中可能无限的列表有单独的类型,并且列表有 ZipList 实例,那会更好。

根据评论,您的实施实际上也很好,只要您在需要时填充两个列表即可。不错!

Applicative [] 具有生成所有可能的组合行为而不是任何类型的活泼行为的基本原因是 ApplicativeMonad 的超类并且旨在根据 Monad 实例存在时的行为。 Monad [] 将列表视为失败和优先选择,因此 Applicative [] 实例也是如此。人们经常使用应用程序接口重构 monadic 代码,以减少值所需的中间名称的数量,并增加并行性的机会。如果这导致功能语义发生重大变化,那将是非常可怕的。

除此之外,事实是,您对 Applicative [] 实例的选择无所适从,如果考虑 empty/nonempty 和 finite/coinductive/infinite 变体,则更是如此。这是为什么?

好吧,正如我在 中提到的,每个 Applicative f 的生命开始时都是 Monoid (f ()),结合数据的 形状 ,在我们开始担心 之前。列表就是一个很好的例子。

[()]基本上就是数字的类型。数字在很多方面都是幺半群。

Monad [] 中提取 Applicative [] 相当于选择由 1*.

生成的幺半群

同时,Applicative ZipList 利用 Haskell 的余归合并等同于选择由无穷大和最小值生成的幺半群。

该问题提出了一个不合法但接近合法的实例。您会注意到 <*> 不是为空函数列表定义的,但对于非空函数列表,它会填充以匹配参数列表。不对称地,当参数 运行 输出时,它会触发 t运行。有点不对劲。

接下来是两个候选修复程序。

一个是t运行两边都空着,然后你必须拿pure = repeat,你有ZipList.

另一种是排除空列表,两边填充。然后你得到 ApplicativeMonoid 上生成的 positive 数由 1 和 maximum 生成。所以它根本不是 ZipList。这就是我在 this answer 中调用 PadMe 的东西。您需要排除 0 的原因是对于 <*> 输出中的每个位置,您需要指向函数及其参数(分别)来自的两个输入中的位置。没东西pad就不能pad

这是一个有趣的游戏。选择数字 Monoid,看看是否可以将其扩展为列表 Applicative