You can define a newtype wrapper like this:
newtype Foo m a = Foo { unFoo :: m a }
If we also want to say "I want Foo
to have the same instances for Functor
, Monad
, Applicative
... that m
has, the only difference being that the methods use Foo m a
instead of m a
" we can use the -XDerivingVia
extension:
{-# LANGUAGE DerivingVia #-}
newtype Foo m a = Foo { unFoo :: m a }
deriving (Functor,Applicative,Monad,MonadIO,GenerateUUID) via m
Any time the compiler complains about a missing instance for Foo
, add it to the deriving
clause.
A slight refinement. Suppose we also wanted to "inherit" Semigroup
and Monoid
instances. For example: IO ()
is a Monoid
, so we might want Foo IO ()
to be a Monoid
as well. We need a separate deriving
clause:
newtype Foo m a = Foo { unFoo :: m a }
deriving (Functor,Applicative,Monad,MonadIO) via m
deriving (Semigroup,Monoid) via (m a)
Why a separate clause? Because the typeclasses have different kinds. Functor
has kind (Type -> Type) -> Constraint
and we are declaring the instance for Foo m
, which has kind Type -> Type
.
Meanwhile, Semigroup
has kind Type -> Constraint
and we are declaring the instance for Foo m a
, the "fully applied" type constructor with kind Type
.