Monad
is not a stock derivable class, like Eq
or Show
. Neither is Functor
, incidentally, but that one you can actually enable via DeriveFunctor
. But no such luck for Monad
.
But wait, your derivation actually works somehow, right? No errors?
That's probably because you have DeriveAnyClass
enabled, which is a handy little extension, but not a particularly clever one. It can't do the heavy lifting for you, all it does is produce an empty instance declaration. No method implementations at all.
Wait, what's the use of that then? Because default methods! Many classes provide default method implementations for any type, or at least with some constraints. The ones I've met most frequently are FromJSON
and ToJSON
, which have default implementations of everything, provided your type has Generic
.
And DeriveAnyClass
is just a slight shortening of the syntax, that's all. A handy thing so you can write data Foo = ... deriving Bar
, relying on all Bar
's default methods.
But Monad
doesn't have default implementation for >>=
, and that's why you get a warning. And, of course, a crash once you try to call that method.
What you really want is GeneralizedNewtypeDeriving
. This extension also allows you to automatically derive any class, but only for newtype
s, and it works by just delegating each method to the wrapped type's implementation. Very handy.
This is exactly what you need: all method implementations for Foo
will be delegated to those for t
.
In your code, I'm assuming from your problem description, you either don't have GeneralizedNewtypeDeriving
enabled at all, or else you have both DerivedAnyClass
and GeneralizedNewtypeDeriving
enabled, in which case DeriveAnyClass
takes precedence (surprise!)
In order to fix the problem, you can either disable DeriveAnyClass
, or, better yet, use DerivingStrategies
. With that extension enabled, you can explicitly tell the compiler which strategy to use for each derivation - DeriveAnyClass
or GeneralizedNewtypeDeriving
:
{-# LANGUAGE DerivingStrategies #-}
deriving newtype instance (MonadTrans t, Monad (t (State St))) => Monad (Foo t)
^^^^^^^
|
this is the important bit
P.S. if both extensions are enabled, you should also get a warning about that.
Foo {unFoo :: t (State St) a}
? – leftaroundaboutDeriveAnyClass
enabled, orGeneralizedNewtypeDeriving
or both? – Fyodor Soikin