I may be naive in my thinking here but I think if the right hand value of a Reader
is an instance of Monoid
then a Monoid
could be defined for Reader
... Here is my implementation:
instance Monoid a => Monoid (Reader r a) where
mempty = pure mempty
mappend ra rb = (<>) <$> ra <*> rb
This however results in the following error:
• Illegal instance declaration for ‘Monoid (Reader r a)’
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use TypeSynonymInstances if you want to disable this.)
• In the instance declaration for ‘Monoid (Reader r a)’
|
413 | instance Monoid a => Monoid (Reader r a) where
| ^^^^^^^^^^^^^^^^^^^
I am unsure what this error actually means, and why I am unable to implement Monoid
for Reader
though I presume it is something to do with Reader
being a higher kinded type?
There are two problems. The first is this:
type Reader r = ReaderT r Identity
For historical reasons, type synonyms are not allowed in instance declarations. This is the
where T is not a synonym.
part of the error. Luckily we can just expand the synonym; this would give us
instance Monoid a => Monoid (ReaderT r Identity a)
but now we would fall afowl of the other part of the error, namely:
All instance types must be of the form (T t1 ... tn)
Specifically, Identity
is not a type variable, so it doesn't fit this form. Again this restriction is in place primarily for historical reasons. You can remove both restrictions by enabling two language extensions:
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
However, in this case it's not needed. The preferable way is to actually use the prescribed form of instance declaration, so:
instance (Applicative f, Monoid m) => Monoid (ReaderT r f m) where
mempty = pure mempty
mappend = liftA2 mappend
This requires no extensions, and works not just for Reader
but for ReaderT
transforming any Applicative
instance.
However it does make an orphan instance; hence you should consider writing another newtype wrapper.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- or, instead of GeneralizedNewtypeDeriving, write the obvious instances by hand
newtype App f m = App { getApp :: f m } deriving (Functor, Applicative)
instance (Applicative f, Monoid m) => Monoid (App f m) where
mempty = pure mempty
mappend = liftA2 mappend
Then you can use App (Reader r) a
as a monoid whenever a
is a monoid. I seem to recall this existed somewhere in the standard libraries already, but I can't find it any more...