一起使用 Maybe 和 Writer
Using Maybe and Writer together
这是我的鸡蛋包装工厂:
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
似乎工作得很好,我可以很好地链接 add
函数。
但是我想记录下每一步添加了多少鸡蛋的日志。所以我这样做:
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
这给了我想要的:我可以看到最终的纸箱和添加 4 个鸡蛋的记录。
但我似乎失去了像以前那样链接 add
函数的能力:
let c = pure(Carton 2) >>= add 4 -- works
let c = pure(Carton 2) >>= add 4 >>= add 2 -- does not work
如何链接我的新启用编写器的 add
函数?有更好的方法吗?
只需将 add
与 MaybeT
合成:
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)
完整示例位于:http://lpaste.net/169070
我会将 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
这将允许您的原始代码
pure (Carton 2) >>= add 4 >>= add 2
按预期工作。
在第一个示例中,表达式中的第二个 >>=
用于 Maybe
的 Monad
实例,而在第二个示例中它来自 Monad
实例Writer
。具体来说,在第一个示例中,>>=
需要一个类型为 Carton -> Maybe Carton
的函数,例如 add 2
,而在第二个示例中,>>=
需要一个类型为 Maybe Carton -> Writer [String] (Maybe Carton)
的函数.在这两个示例中,pure (Carton 2)
>>=add 4
都有效,因为 pure (Carton 2)
的类型为 Maybe Carton
而 add 4
的类型为 Carton -> <something>
,因此您没有问题。将另一个 >>=
添加到表达式会触发错误,因为在第一个示例中,此 >>=
与第一个示例具有相同的类型,而在第二个示例中,它与 >>=
不同。一种解决方案是更改 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
请注意,这意味着您不能再使用 pure (Carton 2)
,但您需要 pure (Just $ Carton 2)
:
> pure (Just $ Carton 2) >>= add 2 >>= add 5
WriterT (Identity (Just (Carton 9),["adding 2","adding 5"]))
说,我建议你使用 monad transformers 来组合 Maybe
和 Writer
因为这是它们的常见用例。您的示例可以重写为
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
您的示例发生了一些变化:
MaybeT m a
是 monad 转换器。在此示例中,m
是 Writer [String]
,a
是 Carton
。对于 运行 我们首先 runMaybeT
的所有内容,它给你一个 Writer [String] (Maybe Carton)
,然后我们调用 runWriter
就像你在你的例子中所做的那样。
- 要在
MaybeT (Writer [String])
中使用 Writer
函数,我们需要 lift
它们。例如lift $ tell ["something"]
return carton
用于return一个Just Carton
而mzero
用于returnNothing
最后一件事:在这个例子中,我们不能用 WriterT [String] Maybe Carton
反过来组合 Maybe
和 Writer
,因为当有超过 12 个鸡蛋时 runWriterT
会 return 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
这是我的鸡蛋包装工厂:
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
似乎工作得很好,我可以很好地链接 add
函数。
但是我想记录下每一步添加了多少鸡蛋的日志。所以我这样做:
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
这给了我想要的:我可以看到最终的纸箱和添加 4 个鸡蛋的记录。
但我似乎失去了像以前那样链接 add
函数的能力:
let c = pure(Carton 2) >>= add 4 -- works
let c = pure(Carton 2) >>= add 4 >>= add 2 -- does not work
如何链接我的新启用编写器的 add
函数?有更好的方法吗?
只需将 add
与 MaybeT
合成:
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)
完整示例位于:http://lpaste.net/169070
我会将 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
这将允许您的原始代码
pure (Carton 2) >>= add 4 >>= add 2
按预期工作。
在第一个示例中,表达式中的第二个 >>=
用于 Maybe
的 Monad
实例,而在第二个示例中它来自 Monad
实例Writer
。具体来说,在第一个示例中,>>=
需要一个类型为 Carton -> Maybe Carton
的函数,例如 add 2
,而在第二个示例中,>>=
需要一个类型为 Maybe Carton -> Writer [String] (Maybe Carton)
的函数.在这两个示例中,pure (Carton 2)
>>=add 4
都有效,因为 pure (Carton 2)
的类型为 Maybe Carton
而 add 4
的类型为 Carton -> <something>
,因此您没有问题。将另一个 >>=
添加到表达式会触发错误,因为在第一个示例中,此 >>=
与第一个示例具有相同的类型,而在第二个示例中,它与 >>=
不同。一种解决方案是更改 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
请注意,这意味着您不能再使用 pure (Carton 2)
,但您需要 pure (Just $ Carton 2)
:
> pure (Just $ Carton 2) >>= add 2 >>= add 5
WriterT (Identity (Just (Carton 9),["adding 2","adding 5"]))
说,我建议你使用 monad transformers 来组合 Maybe
和 Writer
因为这是它们的常见用例。您的示例可以重写为
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
您的示例发生了一些变化:
MaybeT m a
是 monad 转换器。在此示例中,m
是Writer [String]
,a
是Carton
。对于 运行 我们首先runMaybeT
的所有内容,它给你一个Writer [String] (Maybe Carton)
,然后我们调用runWriter
就像你在你的例子中所做的那样。- 要在
MaybeT (Writer [String])
中使用Writer
函数,我们需要lift
它们。例如lift $ tell ["something"]
return carton
用于return一个Just Carton
而mzero
用于returnNothing
最后一件事:在这个例子中,我们不能用 WriterT [String] Maybe Carton
反过来组合 Maybe
和 Writer
,因为当有超过 12 个鸡蛋时 runWriterT
会 return 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