![](/img/trans.png)
[英]How do I use map over a list with do notation - ie avoid type `IO ()' with type `[IO ()]'?
[英]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)
但是,我沒有使用單獨的 function,而是嘗試在主 function 中使用帶有 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 轉換器只是一種定義的類型;
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
所以你會注意到單子f
function 采用一個駐留在Maybe
單子中的值,該單子本身在另一個單子中(在我們的例子中是IO
)。 f
function 做它的事情並將結果包裝回MaybeT ma
。
還有一個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
類型類的成員,因此可以無限期地嵌套它們,並且仍然在單do
表示法下做事。
編輯:答案被否決了,但我不清楚為什么。 如果該方法有問題,請詳細說明我,以便它幫助包括我在內的人們更好地學習一些東西。
趁着編輯 session 的機會,我想添加一個更好的代碼,因為我認為基於Char
的test
可能不是最好的主意,因為它不會考慮負Int
。 因此,讓我們嘗試使用readMaybe
中的Text.Read
,同時使用Maybe
類型。
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.