简体   繁体   English

一起使用Maybe和Writer

[英]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 . 在第一个示例中,表达式中的第二个>>=用于MaybeMonad实例,而在第二个示例中,它来自WriterMonad实例。 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 Cartonadd 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 MaybeWriter因为这是一个常见的用例。 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: 你的例子有一些变化:

  1. MaybeT ma is the monad transformer. MaybeT ma是monad变压器。 In this example, m is Writer [String] and a is Carton . 在这个例子中, mWriter [String]aCarton 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
  2. To use Writer functions in MaybeT (Writer [String]) we need to lift them. 要在MaybeT (Writer [String])使用Writer函数,我们需要lift它们。 For instance lift $ tell ["something"] 例如lift $ tell ["something"]
  3. return carton is used to return a Just Carton while mzero is used to return Nothing return carton用于返回Just Cartonmzero用于返回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 MaybeWriter ,使用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.

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