Looking at the source for Monad:
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
You can see that >> has a default implementation. My question is, is it considered good or bad practice, and why, to include a function/combinator in the typeclass, instead of providing it separately outside of the typeclass?
That is, why not:
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
and somewhere else:
(>>) :: forall a b. m a -> m b -> m b
{-# INLINE (>>) #-}
m >> k = m >>= \_ -> k
As far as I know, there are two main reasons to include “extra” functions:
Efficiency: Sometimes an inefficient generic implementation exists, and the class’s author expects instance-specific implementations to be significantly better. In such cases, including the function in the class with a default implementation means that instances can use optimized versions if they want, but aren’t required to. For a fun example of this, look at
Foldable. This is also true ofMonad.Choice of implementation: Often there are several subsets of the class functions that could be used; including all the potential functions and using default implementations in terms of each other means that an instance can pick some functions to implement and get the rest automatically. This also applies to
Foldable, butEqis a simpler example.