[英]Using Maybe and Writer together
Here is my egg packing factory: 这是我的鸡蛋包装工厂:
type Eggs = Int
data Carton = Carton Eggs deriving Show
add :: Eggs -> Carton -> Maybe Carton
add e (Carton c)
| c + e <= 12 = Just (Carton $ c + e)
| otherwise = Nothing
main = do
print $ pure(Carton 2) >>= add 4 >>= add 4 >>= add 3
Seems to work well, I can nicely chain add
functions. 似乎运作良好,我可以很好地链add
功能。
But I want to record a log of how many eggs were added at every step. 但我想记录每一步添加多少鸡蛋的日志。 So I do this: 所以我这样做:
import Control.Monad.Writer
type Eggs = Int
data Carton = Carton Eggs deriving Show
add :: Eggs -> Carton -> Writer [String] (Maybe Carton)
add e (Carton c)
| c + e <= 12 = do
tell ["adding " ++ show e]
return (Just (Carton $ c + e))
| otherwise = do
tell ["cannot add " ++ show e]
return Nothing
main = do
let c = add 4 $ Carton 2
print $ fst $ runWriter c
mapM_ putStrLn $ snd $ runWriter c
This gives me what I want: I can see the resulting carton and the record for 4 eggs being added. 这给了我想要的东西:我可以看到生成的纸箱和4个鸡蛋的记录。
But I seem to have lost the ability to chain add
functions like I did before: 但我似乎已经失去了链接add
功能的能力,就像我之前做的那样:
let c = pure(Carton 2) >>= add 4 -- works
let c = pure(Carton 2) >>= add 4 >>= add 2 -- does not work
How can I chain my new writer-enabled add
functions? 如何链接我的新编写器add
功能? Is there a better way of doing this? 有没有更好的方法呢?
Just compose add
with MaybeT
: 只需使用MaybeT
撰写add
:
import Control.Trans.Monad.Maybe
test = pure (Carton 2) >>= MaybeT . add 3
>>= MaybeT . add 4
>>= MaybeT . add 5
runTest = do
print $ fst $ runWriter (runMaybeT test)
Full example at: http://lpaste.net/169070 完整示例: http : //lpaste.net/169070
I'd change add
&c to use MaybeT (Writer [String])
: 我将更改add
&c以使用MaybeT (Writer [String])
:
import Control.Monad.Writer
import Control.Monad.Trans.Maybe
type Eggs = Int
data Carton = Carton Eggs deriving Show
main = do
let c = add 4 $ Carton 2
(result, log) = runWriter $ runMaybeT c
print result
mapM_ putStrLn log
add :: Eggs -> Carton -> MaybeT (Writer [String]) Carton
add e (Carton c)
| c + e <= 12 = do
tell ["adding " ++ show e]
return $ Carton $ c + e
| otherwise = do
tell ["cannot add " ++ show e]
mzero
this will allow your original code of 这将允许您的原始代码
pure (Carton 2) >>= add 4 >>= add 2
to work as expected. 按预期工作。
In the first example, the second >>=
in the expression is for the Monad
instance of Maybe
while in the second example it is from the Monad
instance of Writer
. 在第一个示例中,表达式中的第二个>>=
用于Maybe
的Monad
实例,而在第二个示例中,它来自Writer
的Monad
实例。 Specifically, in the first example the >>=
expects a function with type Carton -> Maybe Carton
, like add 2
, while in the second example >>=
expects a function of type Maybe Carton -> Writer [String] (Maybe Carton)
. 具体来说,在第一个例子中, >>=
期望一个类型为Carton -> Maybe Carton
的函数,如add 2
,而在第二个例子中>>=
期望类型为Maybe Carton -> Writer [String] (Maybe Carton)
的函数Maybe Carton -> Writer [String] (Maybe Carton)
。 In both examples pure (Carton 2)
>>= add 4
works because pure (Carton 2)
has type Maybe Carton
and add 4
has type Carton -> <something>
, so you have no problems. 在两个例子中pure (Carton 2)
>> = add 4
作品,因为pure (Carton 2)
有类型Maybe Carton
和add 4
有类型Carton -> <something>
,所以你没有问题。 Adding another >>=
to the expression triggers the error because in the first example this >>=
has the same type of the first one while in the second example it isn't the same >>=
. 在表达式中添加另一个>>=
会触发错误,因为在第一个示例中,这个>>=
具有与第一个类型相同的类型,而在第二个示例中它不是相同的>>=
。 A solution can be to change add
such that it has type Eggs -> Maybe Carton -> Writer [String] (Maybe Carton)
: 一个解决方案可以是更改add
,使其具有类型Eggs -> Maybe Carton -> Writer [String] (Maybe Carton)
:
add :: Eggs -> Maybe Carton -> Writer [String] (Maybe Carton)
add e Nothing = return Nothing
add e (Just (Carton c))
| c + e <= 12 = do
tell ["adding " ++ show e]
return (Just (Carton $ c + e))
| otherwise = do
tell ["cannot add " ++ show e]
return Nothing
note that this means that you cannot use anymore pure (Carton 2)
but you need pure (Just $ Carton 2)
: 请注意,这意味着你不能再使用pure (Carton 2)
但你需要pure (Just $ Carton 2)
:
> pure (Just $ Carton 2) >>= add 2 >>= add 5
WriterT (Identity (Just (Carton 9),["adding 2","adding 5"]))
Said that, I would suggest you to use monad transformers to compose Maybe
and Writer
because this in a common use case for them. 说,我建议你使用monad变换器来Writer
Maybe
和Writer
因为这是一个常见的用例。 Your example could be rewritten as 您的示例可以重写为
import Control.Monad.Trans.Maybe
import Control.Monad.Writer
type Eggs = Int
data Carton = Carton Eggs deriving Show
add :: Eggs -> Carton -> MaybeT (Writer [String]) Carton
add e (Carton c)
| c + e <= 12 = do
lift $ tell ["adding " ++ show e]
return (Carton $ c + e)
| otherwise = do
lift $ tell ["cannot add " ++ show e]
mzero
main = do
let c = return (Carton 2) >>= add 4 >>= add 2
let result = runWriter $ runMaybeT c
print $ fst $ result
mapM_ putStrLn $ snd $ result
A few things have changed from your example: 你的例子有一些变化:
MaybeT ma
is the monad transformer. MaybeT ma
是monad变压器。 In this example, m
is Writer [String]
and a
is Carton
. 在这个例子中, m
是Writer [String]
, a
是Carton
。 To run everything we first runMaybeT
, which gives you a Writer [String] (Maybe Carton)
, and then we call runWriter
on it like you have done in your example. 要运行我们首先运行的所有内容runMaybeT
,它会给你一个Writer [String] (Maybe Carton)
,然后我们像你在你的例子中那样调用runWriter
。 Writer
functions in MaybeT (Writer [String])
we need to lift
them. 要在MaybeT (Writer [String])
使用Writer
函数,我们需要lift
它们。 For instance lift $ tell ["something"]
例如lift $ tell ["something"]
return carton
is used to return a Just Carton
while mzero
is used to return Nothing
return carton
用于返回Just Carton
而mzero
用于返回Nothing
One last thing: in this example we cannot compose Maybe
and Writer
the other way around, with WriterT [String] Maybe Carton
, because when there are more than 12 eggs runWriterT
would return Nothing
and suppress the history: 最后一件事:在这个例子中,我们不能用其他方式Writer
Maybe
和Writer
,使用WriterT [String] Maybe Carton
,因为当有超过12个鸡蛋时, runWriterT
将返回Nothing
并抑制历史记录:
import Control.Monad
import Control.Monad.Trans
import Control.Applicative
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Writer
type Eggs = Int
data Carton = Carton Eggs deriving Show
add :: Eggs -> Carton -> WriterT [String] Maybe Carton
add e (Carton c)
| c + e <= 12 = do
tell ["adding " ++ show e]
lift $ Just $ Carton $ c + e
| otherwise = do
tell ["cannot add " ++ show e]
lift Nothing
main = do
let c = return (Carton 2) >>= add 4 >>= add 20
case runWriterT c of
Nothing ->
print "nothing to print"
Just (carton, history) -> do
print carton
mapM_ putStrLn $ history
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.