Monads是實現monad操作的任何多態數據類型m a
,return
和>>=
,並服從monad法則。
一些單子有一個特殊的形式,因爲m
可以寫成多態mT m'
,並且只要參數m'
是單子,它將是單子。我們可以像這樣拆分的Monad是monad變形金剛。外層monad mT
,增加了monadic效果的內部monad。由於內部m'
,我們可以嵌套無限量的monads,本身可以是monad變壓器。
由於Maybe是最簡單的monads之一,因此我將轉發代碼Transformers。
該定義顯示monad MaybeT m
大部分是圍繞monad m
的包裝。但是,m
不再是「純」monad,而是具有受Maybe影響的類型參數。
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
現在定義Monad實例。
instance (Monad m) => Monad (MaybeT m) where
return = lift . return
x >>= f = MaybeT $ do
v <- runMaybeT x
case v of
Nothing -> return Nothing
Just y -> runMaybeT (f y)
望着綁定操作>>=
,一定要注意以下的$
該做記號發生在m
單子是很重要的。內部monad通過runMaybeT x
恢復,monadic值綁定到v
,觸發m
效果。然後,評估Maybe
狀態,將f
應用於某個值(如果存在)並適當包裝。
對我來說,混淆的一個來源是內部和外部monad的術語 - 如果我將它倒退,我不會感到驚訝。轉換後的monad mT
實際上是將自己投射到內部monad中,m
。
該問題詢問了關於lift
,它對應於在外部monad的純上下文中運行內部monad的能力。需要注意的是lift
或MonadTrans沒有定義的變壓器,但是他說,如果一個單子(t m)
是變壓器,那麼你應該能夠解除m a
成純(t m) a
在我的例子,下面是一些一樣機程序,其中用戶 要求一些資源。函數userGetResource
要求輸入用戶名,然後在某個註冊表中查詢該名稱,如果找到該名稱,它將嘗試獲取該用戶的權限,如果給予該用戶權限,它將返回該資源。有一系列的IO操作可能會因爲Nothing
而失敗。 MaybeT有助於編寫函數,使其更易於閱讀和維護。特別注意在userGetResource
函數中使用lift
。因爲它總是會返回一個字符串(baring catastrophe),所以這個函數被解析成MaybeT的純粹Just
形式。
import Data.List(find)
import Control.Monad (liftM)
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class(lift)
data User = User { userName :: String, hasCredentials :: Credentials }
type Credentials = Bool
type Token =()
type UserReg = [User]
data Resource = Resource deriving Show
userGetResource :: IO (Maybe Resource)
userGetResource = runMaybeT $ do
str <- lift $ do putStrLn "Who are you"
getLine
usr <- MaybeT $ getUser str
tok <- MaybeT $ getPermission usr
MaybeT $ getResource tok
getResource :: Token -> IO (Maybe Resource)
getResource _ = return (Just Resource)
userRegistry :: IO UserReg
userRegistry = return [User "Alice" True, User "Bob" False]
lookupUser :: String -> UserReg -> Maybe User
lookupUser name = find ((name==) . userName)
getUser :: String -> IO (Maybe User)
getUser str = do
reg <- userRegistry
return $ lookupUser str reg
getPermission :: User -> IO (Maybe Token)
getPermission usr
| hasCredentials usr = do
tok <- generateToken
return (Just tok)
| otherwise = return Nothing
generateToken :: IO Token
generateToken = doSomeUsefulIO
where
doSomeUsefulIO = return()
而且這裏是幾個電話的輸出userGetResource
失敗, 「薩姆」 不是新用戶註冊
*MaybeTrans> userGetResource
Who are you
Sam
Nothing
成功, 「愛麗絲」 在註冊表和有權限。
*MaybeTrans> userGetResource
Who are you
Alice
Just Resource
失敗。 「Bob」正在註冊,但沒有權限。
*MaybeTrans> userGetResource
Who are you
Bob
Nothing
感謝您的回答,非常清楚! –