簡體   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