簡體   English   中英

如何在 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)

但是,我沒有使用單獨的 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)}

實際上,像MaybeTStateT等的變壓器在Control.Monad.Trans.MaybeControl.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 的機會,我想添加一個更好的代碼,因為我認為基於Chartest可能不是最好的主意,因為它不會考慮負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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM