class Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
{-# INLINE (>>) #-}
m >> k = m >>= \_ -> k -- <-- !! right here !!
fail s = error s
您可以看到>>
具有默认实现。我的问题是,在类型类中包含一个函数/组合器,而不是在类型类之外单独提供它是不是被认为是好的或坏的做法,为什么呢?
那就是为什么不:
class Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
return :: a -> m a
fail :: String -> m a
fail s = error s
以及其他地方:
(>>) :: forall a b. m a -> m b -> m b
{-# INLINE (>>) #-}
m >> k = m >>= \_ -> k
答案 0 :(得分:13)
据我所知,包含“额外”功能有两个主要原因:
效率:有时存在效率低下的通用实现,并且该类的作者期望特定于实例的实现明显更好。在这种情况下,在类中包含具有默认实现的函数意味着实例可以根据需要使用优化版本,但不是必需的。有关这方面的有趣示例,请查看Foldable
。 Monad
也是如此。
实施的选择:通常可以使用几个类函数的子集;包括所有潜在的函数并使用默认实现相互意味着实例可以选择一些函数来实现并自动获取其余的函数。这也适用于Foldable
,但Eq
是一个更简单的示例。
答案 1 :(得分:3)
这样,自定义>>
可以为monad实现,可以通过m >>= \_ -> k
更高效或自然地完成,但默认实现仍然存在。
答案 2 :(得分:3)
在类型类中包含方法的另一个论点是,它们应该满足某些法律,或者当它们使这些法律的陈述更清楚时。我认为法律应该在道德上与类型类相关联(“为了声明这个类的实例,我必须提供什么?”)例如,你可能更愿意用{{1}来陈述monad定律。 },return
和join
,而不是fmap
和return
;鼓励您将所有四个运算符放在类型类中(并使>>=
成为Monad
的子类!),并根据Functor
和>>=
给出join
的默认定义反之亦然。