简体   繁体   English

(Monoid a) 没有因使用“mempty”而产生的实例

[英]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) ?为什么Monoid a明确表示Monoid a => Monoid (Logger a)时没有实例? If my Logger a type in instantiated as Monoid why can't I use mempty in fail definition?如果我的Logger a实例化为Monoid的类型,为什么我不能在fail定义中使用 mempty? What am I missing?我错过了什么?

The error is not complaining about your Monoid instance.该错误不是在抱怨您的Monoid实例。 That part is fine.那部分很好。

What it is complaining about is your use of that instance in the definition of your MonadFail instance.抱怨的是您在MonadFail实例的定义中使用该实例。 When you write当你写

fail msg = mempty

then GHC selects the Monoid (Logger a) instance.然后 GHC 选择Monoid (Logger a)实例。 But that instance has a constraint on it, namely Monoid a .但是那个实例有一个约束,即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.这意味着必须知道在使用mempty的地方有a Monoid实例,在这种情况下,它位于您的MonadFail实例中。

The root problem here is that fail has a very general type signature:这里的根本问题是fail有一个非常通用的类型签名:

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 .请注意,这里的a是通用量化的,这意味着调用者可以将其实例化为他们想要的任何类型 This means they could pick a type like Void , which does not have a Monoid instance.这意味着他们可以选择像Void这样没有Monoid实例的类型。 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.因此,您的Logger简单类型不能支持MonadFail实例,因为它需要生成一个完全任意类型的值a ,而且它没有办法这样做。

Alexis King explains very well what the problem is. Alexis King 很好地解释了问题所在。 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 = []} .现在你可以写fail m = Logger {theResult = Left m, theLog = []}

Another option, which I think is more common, is to make your logger a monad transformer :我认为更常见的另一种选择是使您的记录器成为monad 转换器:

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:现在你可以从下面的 monad 中解除失败:

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 .您可以使用Control.Monad.Trans.Writer.Strict或(在许多/大多数情况下更好) Control.Monad.Trans.Writer.CPS ,而不是自己编写所有内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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