简体   繁体   中英

No instance for (Monoid a) arising from a use of ‘mempty’

I have the following code:

data Logger a = Logger { getLog :: (a, [String]) }

instance Functor Logger where
    fmap f (Logger (x, log)) = Logger (f x, log)

instance Applicative Logger where
    pure x = Logger (x, [])
    (Logger (f, log1)) <*> (Logger (x, log2)) = Logger (f x, log1 `mappend` log2)

instance Semigroup a => Semigroup (Logger a) where
    (Logger (x, log1)) <> (Logger (y, log2)) =  Logger (x <> y, log1 <> log2)

instance Monoid a => Monoid (Logger a) where
    mempty = Logger (mempty, [])

instance Monad Logger where
    return x = Logger (x, [])
    (Logger (x, log)) >>= f = Logger (res, log `mappend` newLog)
                                where (Logger (res, newLog)) = f x

instance Fail.MonadFail Logger where
    fail msg = mempty

I get the following error:

• No instance for (Monoid a) arising from a use of ‘mempty’
  Possible fix:
    add (Monoid a) to the context of
      the type signature for:
        Fail.fail :: forall a. String -> Logger a
• In the expression: mempty
  In an equation for ‘Fail.fail’: Fail.fail msg = mempty
  In the instance declaration for ‘Fail.MonadFail Logger’

Why is there no instance for Monoid a when it clearly says Monoid a => Monoid (Logger a) ? If my Logger a type in instantiated as Monoid why can't I use mempty in fail definition? What am I missing?

The error is not complaining about your Monoid instance. That part is fine.

What it is complaining about is your use of that instance in the definition of your MonadFail instance. When you write

fail msg = mempty

then GHC selects the Monoid (Logger a) instance. But that instance has a constraint on it, namely Monoid a . That means a must be known to have a Monoid instance at the place where mempty is used , which in this case is inside your MonadFail instance.

The root problem here is that fail has a very general type signature:

fail :: forall m a. MonadFail m => String -> m a

Note that the a here is universally quantified, which means the caller is allowed to instantiate it to any type they want . This means they could pick a type like Void , which does not have a Monoid instance. Therefore, your Logger simply type cannot support a MonadFail instance, since it would need to produce a value of a completely arbitrary type a , and there is no way for it to do so.

Alexis King explains very well what the problem is. What are some potential solutions? One option is to include failure explicitly:

data Logger a = Logger
  { theResult :: Either String a
  , theLog :: [String] }

Now you could write fail m = Logger {theResult = Left m, theLog = []} .

Another option, which I think is more common, is to make your logger a monad transformer :

newtype Logger m a = Logger
  { runLogger :: m (a, [String]) }

instance MonadTrans Logger where
  lift m = Logger $ do
    a <- m
    pure (a, [])

Now you can lift failure from the monad below:

instance MonadFail m => MonadFail (Logger m) where
  fail = lift . fail

Rather than writing everything yourself, you can use Control.Monad.Trans.Writer.Strict , or (in many/most cases better) Control.Monad.Trans.Writer.CPS .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM