简体   繁体   English

在Haskell中将Writer monad与Conduit结合使用

[英]Using the Writer monad with Conduit in Haskell

As an exercise for learning Haskell, Conduit and Monads, I want to create a conduit that tells the input value and passes it through. 作为学习Haskell,Conduit和Monads的练习,我想创建一个告诉输入值并将其传递的管道。

Code is pretty simple, but I'm getting compilation errors that are still cryptic for me: 代码非常简单,但是我遇到的编译错误对我来说仍然是个隐秘的问题:

 log =
    await >>= \case
      Nothing -> return ()
      Just value -> do
        tell [value]
        yield value

 runWriter $ CL.sourceList ["a", "b"] $= log $$ CL.consume

And the error: 错误:

 No instance for (MonadWriter [o0] m0) arising from a use of ‘tell’
 The type variables ‘m0’, ‘o0’ are ambiguous
 Relevant bindings include
   value :: o0
     (bound at /home/vagrant/workspace/dup/app/Main.hs:241:10)
   logg :: ConduitM o0 o0 m0 ()
     (bound at /home/vagrant/workspace/dup/app/Main.hs:238:1)
 Note: there are several potential instances:
   instance MonadWriter w m => MonadWriter w (ConduitM i o m)
     -- Defined in ‘conduit-1.2.6.4:Data.Conduit.Internal.Conduit’
   instance MonadWriter w m =>
            MonadWriter
              w (conduit-1.2.6.4:Data.Conduit.Internal.Pipe.Pipe l i o u m)
     -- Defined in ‘conduit-1.2.6.4:Data.Conduit.Internal.Pipe’
   instance [safe] MonadWriter w m =>
                   MonadWriter w  (Control.Monad.Trans.Resource.Internal.ResourceT m)
     -- Defined in ‘Control.Monad.Trans.Resource.Internal’
   ...plus 11 others
 In a stmt of a 'do' block: tell [value]
 In the expression:
  do { tell [value];
       yield value }
 In a case alternative:
    Just value
      -> do { tell [value];
              yield value }

Here's what works for me: 这对我有用:

{-# LANGUAGE FlexibleContexts #-}

import Data.Conduit
import Control.Monad.Writer
import qualified Data.Conduit.List as CL

doit :: MonadWriter [i] m => Conduit i m i
doit = do
  x <- await
  case x of
    Nothing -> return ()
    Just v  -> do tell [v]; yield v; doit

foo = runWriter $ CL.sourceList ["a", "b", "c"] =$= doit $$ CL.consume

Note I changed the name from log to doit to avoid the name clash with Prelude.log . 注意:我从更名log ,以doit ,以避免名称冲突与Prelude.log

Update 更新

If you start with: 如果您从以下内容开始:

import Data.Conduit
import Control.Monad.Writer
import qualified Data.Conduit.List as CL

doit = do
  x <- await
  case x of
    Nothing -> return ()
    Just v -> do tell [v]; yield v; doit

you will get two errors: 您将收到两个错误:

No instance for (Monad m0) arising from a use of ‘await’
...

No instance for (MonadWriter [o0] m0) arising from a use of ‘tell’
...

Since doit is a top-level function, experience will tell you that perhaps the monomorphism restriction is at work here. 由于doit是顶级功能,经验会告诉您,单态性限制可能在这里起作用。 Indeed, after adding: 确实,在添加之后:

{-# LANGUAGE NoMonomorphismRestriction #-}

you only get one error: 您只会得到一个错误:

Non type-variable argument in the constraint: MonadWriter [o] m
(Use FlexibleContexts to permit this)
...

And after adding FlexibleContexts , the code compiles. 在添加FlexibleContexts ,代码将进行编译。

Now you can interrogate the type of doit : 现在您可以询问doit的类型:

ghci> :t doit
doit :: MonadWriter [o] m => ConduitM o o m ()

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

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