适用函子为单曲面函子

V. *_*ria 4 haskell applicative

适用函数的黑客中提到的那样,它们是强大的松散单曲面函数。那么,为什么它们在Haskell中的定义却不这样显示:

class Functor f => MonoidalApplicative f where
  mult :: f a -> f b -> f (a,b)
  unit :: a -> f a 

  starAp :: f (a -> b) -> f a -> f b
  starAp h x = fmap (uncurry ($)) (mult h x)
Run Code Online (Sandbox Code Playgroud)

<*>(starAp)可以很容易地按照乘法来重构,这个定义对我来说更简单。例如,这是Maybe实例:

instance MonoidalApplicative Maybe where
  mult (Just x) (Just y) = Just (x,y)
  mult _ _ = Nothing

  unit x = Just x
Run Code Online (Sandbox Code Playgroud)

She*_*rsh 5

因为它是在评论你的答案中提到,有类似的故事join>>=。当有几种语义上等效的方法来定义某些内容时,最好总是选择最有效和务实的方法。Haskell的目的是编写代码,而不是为了证明事物(尽管不幸的是,Haskell仍未成为非常流行的编程语言)。

如果starAp具有默认实现,几乎没有人会实现它(就像现在>>Monadtype类中那样)。但是<*>是极其有用的操作。它在applicate与一元解析器使用了很多(megaparsecattoparsecoptparse-applicative),我不能想象我的生活W / O liftA*加盟的事情。对于此操作而言,尽可能高效是非常重要的。实施starApas fmap (uncurry ($)) (mult h x)可能会给内联和优化编译器带来困难。

而且,Applicative使用multunit操作的表示并不能真正解决任何问题。显然,mult = liftA2 (,)。但是你的实现

mult (Just x) (Just y) = Just (x,y)

不完全正确。因为您的实现还不够懒。当可能只评估一种情况时,您将评估两种情况。因此,即使使用此简单功能,您仍然可以弄乱。因此,这种表示方式严格来说更糟。

  • 因此,这不仅是性能问题,而且还涉及正确性。从某种意义上说,Haskell的分类定义是“这是一种编程语言,从严格的数学意义上来说可能不正确”的声明。 (2认同)