如何对 Maybe 和 IO 使用 Do 表示法
How to use Do notation with both Maybe and IO
我正在努力掌握 Haskell 中的 do notation
。
我可以将它与 Maybe 一起使用,然后打印结果。像这样:
maybeAdd :: Maybe Integer
maybeAdd = do one <- maybe1
two <- maybe2
three <- maybe3
return (one + two + three)
main :: IO ()
main = putStr (show $ fromMaybe 0 maybeAdd)
但是我没有使用单独的函数,而是尝试在主函数中使用带有 Maybe 的 do 表示法。但我没有任何运气。我尝试过的各种尝试包括:
main :: IO ()
main = do one <- maybe1
two <- maybe2
three <- maybe3
putStr (show $ fromMaybe 0 $ return (one + two + three))
main :: IO ()
main = do one <- maybe1
two <- maybe2
three <- maybe3
putStr (show $ fromMaybe 0 $ Just (one + two + three))
main :: IO ()
main = do one <- maybe1
two <- maybe2
three <- maybe3
putStr (show $ (one + two + three))
所有这些都会导致各种类型的编译错误,不幸的是我没能破译正确的方法。
如何实现上述目标?也许可以解释为什么我尝试的方法也是错误的?
每个 do
块必须在单个 monad 中工作。如果你想使用多个 monad,你可以使用多个 do
块。尝试调整您的代码:
main :: IO ()
main = do -- IO block
let x = do -- Maybe block
one <- maybe1
two <- maybe2
three <- maybe3
return (one + two + three)
putStr (show $ fromMaybe 0 x)
你甚至可以使用
main = do -- IO block
putStr $ show $ fromMaybe 0 $ do -- Maybe block
one <- maybe1
two <- maybe2
three <- maybe3
return (one + two + three)
-- other IO actions here
但在某些情况下可读性较差。
MaybeT
monad 转换器在这种特殊情况下会派上用场。 MaybeT
monad transformer 只是一个类型定义类似;
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
实际上像 MaybeT
、StateT
等转换器在 Control.Monad.Trans.Maybe
、Control.Monad.Trans.State
中很容易获得...出于说明目的,它的 Monad 实例可能类似于如下所示;
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
x >>= f = MaybeT $ runMaybeT x >>= g
where
g Nothing = return Nothing
g (Just x) = runMaybeT $ f x
所以你会注意到 monadic f
函数采用驻留在 Maybe
monad 中的值,而 Maybe
monad 本身位于另一个 monad 中(IO
在我们的例子中)。 f
函数完成它的工作并将结果包装回 MaybeT m a
.
还有一个 MonadTrans
class,您可以在其中获得一些转换器类型使用的常用功能。其中之一是 lift
,它用于根据特定实例的定义将值提升到转换器中。对于 MaybeT
它应该看起来像
instance MonadTrans MaybeT where
lift = MaybeT . (liftM Just)
让我们用 monad 转换器执行你的任务。
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
guard $ test i
j <- lift getLine
guard $ test j
lift . print $ (read i :: Int) + (read j :: Int)
where
test = and . (map isDigit)
所以当被称为
λ> runMaybeT addInts
Enter two integers..
1453
1571
3024
Just ()
要注意的是,由于 monad 转换器也是 Monad
类型 class 的成员,因此可以无限期地嵌套它们,并且仍然在单个 do
表示法下做事。
编辑: 答案被否决,但我不清楚原因。如果方法有问题,请仔细说明我,以便帮助包括我在内的人更好地学习。
利用编辑 session 的机会,我想添加一个更好的代码,因为我认为基于 Char
的 test
ing 可能不是最好的主意,因为它不会考虑负 Int
s。因此,让我们在使用 Maybe
类型时尝试使用 Text.Read
包中的 readMaybe
。
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class (lift)
import Text.Read (readMaybe)
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
MaybeT $ return (readMaybe i :: Maybe Int)
j <- lift getLine
MaybeT $ return (readMaybe j :: Maybe Int)
lift . print $ (read i :: Int) + (read j :: Int)
我想现在效果更好了...
λ> runMaybeT addInts
Enter two integers..
-400
500
100
Just ()
λ> runMaybeT addInts
Enter two integers..
Not an Integer
Nothing
我正在努力掌握 Haskell 中的 do notation
。
我可以将它与 Maybe 一起使用,然后打印结果。像这样:
maybeAdd :: Maybe Integer
maybeAdd = do one <- maybe1
two <- maybe2
three <- maybe3
return (one + two + three)
main :: IO ()
main = putStr (show $ fromMaybe 0 maybeAdd)
但是我没有使用单独的函数,而是尝试在主函数中使用带有 Maybe 的 do 表示法。但我没有任何运气。我尝试过的各种尝试包括:
main :: IO ()
main = do one <- maybe1
two <- maybe2
three <- maybe3
putStr (show $ fromMaybe 0 $ return (one + two + three))
main :: IO ()
main = do one <- maybe1
two <- maybe2
three <- maybe3
putStr (show $ fromMaybe 0 $ Just (one + two + three))
main :: IO ()
main = do one <- maybe1
two <- maybe2
three <- maybe3
putStr (show $ (one + two + three))
所有这些都会导致各种类型的编译错误,不幸的是我没能破译正确的方法。
如何实现上述目标?也许可以解释为什么我尝试的方法也是错误的?
每个 do
块必须在单个 monad 中工作。如果你想使用多个 monad,你可以使用多个 do
块。尝试调整您的代码:
main :: IO ()
main = do -- IO block
let x = do -- Maybe block
one <- maybe1
two <- maybe2
three <- maybe3
return (one + two + three)
putStr (show $ fromMaybe 0 x)
你甚至可以使用
main = do -- IO block
putStr $ show $ fromMaybe 0 $ do -- Maybe block
one <- maybe1
two <- maybe2
three <- maybe3
return (one + two + three)
-- other IO actions here
但在某些情况下可读性较差。
MaybeT
monad 转换器在这种特殊情况下会派上用场。 MaybeT
monad transformer 只是一个类型定义类似;
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
实际上像 MaybeT
、StateT
等转换器在 Control.Monad.Trans.Maybe
、Control.Monad.Trans.State
中很容易获得...出于说明目的,它的 Monad 实例可能类似于如下所示;
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
x >>= f = MaybeT $ runMaybeT x >>= g
where
g Nothing = return Nothing
g (Just x) = runMaybeT $ f x
所以你会注意到 monadic f
函数采用驻留在 Maybe
monad 中的值,而 Maybe
monad 本身位于另一个 monad 中(IO
在我们的例子中)。 f
函数完成它的工作并将结果包装回 MaybeT m a
.
还有一个 MonadTrans
class,您可以在其中获得一些转换器类型使用的常用功能。其中之一是 lift
,它用于根据特定实例的定义将值提升到转换器中。对于 MaybeT
它应该看起来像
instance MonadTrans MaybeT where
lift = MaybeT . (liftM Just)
让我们用 monad 转换器执行你的任务。
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
guard $ test i
j <- lift getLine
guard $ test j
lift . print $ (read i :: Int) + (read j :: Int)
where
test = and . (map isDigit)
所以当被称为
λ> runMaybeT addInts
Enter two integers..
1453
1571
3024
Just ()
要注意的是,由于 monad 转换器也是 Monad
类型 class 的成员,因此可以无限期地嵌套它们,并且仍然在单个 do
表示法下做事。
编辑: 答案被否决,但我不清楚原因。如果方法有问题,请仔细说明我,以便帮助包括我在内的人更好地学习。
利用编辑 session 的机会,我想添加一个更好的代码,因为我认为基于 Char
的 test
ing 可能不是最好的主意,因为它不会考虑负 Int
s。因此,让我们在使用 Maybe
类型时尝试使用 Text.Read
包中的 readMaybe
。
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class (lift)
import Text.Read (readMaybe)
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
MaybeT $ return (readMaybe i :: Maybe Int)
j <- lift getLine
MaybeT $ return (readMaybe j :: Maybe Int)
lift . print $ (read i :: Int) + (read j :: Int)
我想现在效果更好了...
λ> runMaybeT addInts
Enter two integers..
-400
500
100
Just ()
λ> runMaybeT addInts
Enter two integers..
Not an Integer
Nothing