This only works because Snap
has a Monad
instance (and it's actually in scope at that point).
Effectively, the compiler handles declarations in two separate passes: first it resolves all the instance heads
instance Applicative Snap
instance Monad Snap
...without even looking in the actual method implementations. This works out fine: Monad
is happy as long as it sees the Applicative
instance.
So then it already knows that Snap
is a monad. Then it proceeds to typecheck the (<*>)
implementation, notices that it requires the Monad
instance, and... yeah, it's there, so that too is fine.
The actual reason we have ap :: Monad m => ...
is mostly historical: the Haskell98 Monad
class did not have Applicative
or even Functor
as a superclass, so it was possible to write code Monad m => ...
that could then not use fmap
or <*>
. Therefore the liftM
and ap
functions were introduced as replacement.
Then, when the better current class hierarchy was established, many instances were simply defined by referring back to the already existing Monad
instance, which is after all sufficient for everything.
IMO it is usually a good idea to directly implement <*>
and definitely fmap
before writing the Monad
instance, rather than the other way around.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…