繁体   English   中英

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

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

我有以下代码:

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

我收到以下错误:

• 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’

为什么Monoid a明确表示Monoid a => Monoid (Logger a)时没有实例? 如果我的Logger a实例化为Monoid的类型,为什么我不能在fail定义中使用 mempty? 我错过了什么?

该错误不是在抱怨您的Monoid实例。 那部分很好。

抱怨的是您在MonadFail实例的定义中使用该实例。 当你写

fail msg = mempty

然后 GHC 选择Monoid (Logger a)实例。 但是那个实例有一个约束,即Monoid a 这意味着必须知道在使用mempty的地方有a Monoid实例,在这种情况下,它位于您的MonadFail实例中。

这里的根本问题是fail有一个非常通用的类型签名:

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

请注意,这里的a是通用量化的,这意味着调用者可以将其实例化为他们想要的任何类型 这意味着他们可以选择像Void这样没有Monoid实例的类型。 因此,您的Logger简单类型不能支持MonadFail实例,因为它需要生成一个完全任意类型的值a ,而且它没有办法这样做。

Alexis King 很好地解释了问题所在。 有哪些潜在的解决方案? 一种选择是明确包括失败:

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

现在你可以写fail m = Logger {theResult = Left m, theLog = []}

我认为更常见的另一种选择是使您的记录器成为monad 转换器:

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

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

现在你可以从下面的 monad 中解除失败:

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

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

暂无
暂无

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

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