正如Hackage for Applicative Functors中所提到的,它们是强大的松散幺半群仿函数。那么为什么他们在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)
<*>
(starAp)很容易根据乘法重建,这个定义对我来说更简单。例如,这是Maybe实例:
instance MonoidalApplicative Maybe where
mult (Just x) (Just y) = Just (x,y)
mult _ _ = Nothing
unit x = Just x
答案 0 :(得分:5)
正如您在回答的评论中提到的那样,join
和>>=
也有类似的故事。如果有几种语义上相同的方式来定义某些内容,那么最好总是选择最有效的方法。务实的方式。 Haskell的目的是编写代码,而不是为了证明事物(尽管如此,不幸的是, Haskell 还没有成为非常流行的编程语言)。
如果starAp
有默认实现,几乎没有人会实现它(就像现在>>
类型类中的Monad
一样)。但是<*>
是非常有用的操作。它用于应用和monadic解析器很多(megaparsec
,attoparsec
,optparse-applicative
),我无法想象我的生活没有liftA*
加入事物。并且这项操作尽可能高效非常重要。将starAp
实现为fmap (uncurry ($)) (mult h x)
可能会给编译器带来内联和优化的困难。
此外,使用Applicative
和mult
操作代表unit
并不能解决任何问题。显然,mult = liftA2 (,)
。但是你的实现是
mult (Just x) (Just y) = Just (x,y)
不完全正确。因为你的实现不够懒惰。您将评估这两种情况,只需评估一种情况就足够了。所以即使使用这个简单的功能你仍然会陷入困境。因此,这种表现形式严重恶化。