为什么列表Applicative实例不执行一对一应用程序?

时间:2018-06-05 13:51:27

标签: haskell applicative

我在Haskell的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的方式定义。 我想第一个定义可能更有用吗?这个选择有什么具体原因吗?

2 个答案:

答案 0 :(得分:5)

这基本上是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在实践中有点滑稽。标准实现基本上是最自然的仅限有限实现。可以说,如果前奏中存在有限数组和可能无限列表的单独类型,并且列表中包含ZipList实例,则会更好。

通过评论,你的实现实际上也很好,如果你只需要填写两个列表。尼斯!

答案 1 :(得分:5)

Applicative []具有generate-all-possible-combination行为而不是任何类型的zippy行为的基本原因是ApplicativeMonad的超类,并且打算当一个存在时,按照Monad实例行事。 Monad []将列表视为失败和优先级选择,因此Applicative []实例也会这样做。人们经常使用应用程序界面重构monadic代码,以减少值所需的中间名称的数量,并增加并行机会。如果这导致功能语义发生重大转变,那将是非常可怕的。

除此之外,事实是,你为Applicative []个实例的选择而被宠坏了,如果考虑空/非空和有限/共同/无限变化,你会更加如此。那是为什么?

好吧,正如我在this answer中提到的那样,每个Applicative f都以Monoid (f ())开头,结合数据的形状,然后才开始担心。列表就是一个很好的例子。

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

Applicative []开始Monad []相当于选择由1*生成的幺半群。

与此同时,Applicative ZipList利用了Haskell的共感混淆,相当于选择由无穷大和最小值生成的幺半群。

这个问题提出了一个不合法的例子,但是接近一个例子。您会注意到<*>没有为一个空的函数列表定义,但对于非空的函数列表,它会填充以匹配参数列表。不对称,它在参数用完时截断。事情不太对劲。

接下来是两个候选修正案。

一个是截断空两边,然后你必须pure = repeatZipList

另一种是排除空列表并在两侧填充。然后,您将获得{1}生成的Applicative生成的Monoid 数字生成的ZipList最大。所以它根本不是PadMe。这就是我在this answer中调用<*>的内容。你需要排除0的原因是,对于Monoid输出中的每个位置,你需要指向两个输入中的位置,其中函数及其参数(分别)来自。如果你没有任何东西可以填充,你就无法填补。

这是一款有趣的游戏。在数字上选择Applicative,看看您是否可以将其增长为 void enqueue( int item) { if (is Full(this)) return; this.rear = (this.rear + 1)%this.capacity; this.array[this.rear] = item; this.size = this.size + 1; System.out.println(item+ " enqueued to queue"); } 列表!