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



我从Hutton's Programming in 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的方式定义. 我想第一个定义可能更有用?这种选择有什么具体的原因吗?

这基本上是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实例,那就更好了。

通过评论,您的实现实际上也很好,只要在需要时填充两个列表即可。好!

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

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

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

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

Monad []中获取Applicative []等于选择由1*生成的幺半群。

同时,Applicative ZipList利用了哈斯克尔的归纳合并,相当于选择了由无穷大和最小值生成的幺半群。

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

下面是两个候选修复。

一种是截断两侧的空,然后你必须拿pure = repeat,你就ZipList.

另一种是排除空列表并在两侧填充。然后,您将获得由 1 和最大值生成的Monoid生成的Applicative。所以这根本不ZipList。这就是我在这个答案中称PadMe的事情。您需要排除 0 的原因是,对于<*>输出中的每个位置,您需要指向两个输入中函数及其参数(分别(来自的位置。如果你没有什么可垫的,你就不能垫。

这是一个有趣的游戏。选择一个数字Monoid,看看你是否可以把它发展成一个列表Applicative

最新更新