[英]Recursive liftIO
我已經看過MonadTrans的一些實例,對於MaybeT,實現看起來像這樣:
instance MonadTrans MaybeT where
lift = MaybeT . liftM Just
據我所知,MonadIO的實例用於從內部進行可變數量的提升,一個IO monad,直接到最外層。 對於MaybeT案例,它看起來像這樣:
instance (MonadIO m) => MonadIO (MaybeT m) where
liftIO = lift . liftIO
我不明白的是這個遞歸函數如何逃避無限循環。 什么是基本情況?
也許令人驚訝的是,下面的定義不是遞歸的,即使看起來如此。
instance (MonadIO m) => MonadIO (MaybeT m) where
liftIO = lift . liftIO
這是因為liftIO
左手側是liftIO
為MaybeT m
單子,而liftIO
右手邊是liftIO
為m
單子。
因此,這種簡單的定義liftIO
在一個單子在方面liftIO
另一個單子。 這里沒有遞歸。
這類似於例如
instance (Show a, Show b) => Show (a,b) where
show (x,y) = "(" ++ show x ++ ", " ++ show y ++ ")"
上面,我們定義如何打印一對,具體取決於如何打印其組件。 它看起來像遞歸,但事實並非如此。
它可以通過插入顯式類型參數來幫助可視化,至少在心理上:
-- pseudo-code
instance (Show a, Show b) => Show (a,b) where
show @(a,b) (x,y) =
"(" ++ show @a x ++ ", " ++ show @b y ++ ")"
現在show @(a,b)
, show @a
, show @b
是不同的函數。
對某些專業化進行簡單的等式推理和重寫定義可以幫助您。 MonadIO
基本案例是IO
。 MaybeT
是monad變換器,所以讓我們在一些簡單的例子中結合MaybeT
和IO
。
foo :: MaybeT IO String
foo = liftIO getLine
現在讓我們一步一步地重寫這個函數定義,從你的問題中應用實例實現。
foo
= liftIO {- for MaybeT -} getLine
= lift (liftIO {- for IO here -} getLine) -- step 2
= lift (id getLine)
= lift getLine
= MaybeT (liftM Just getLine)
getLine
具有類型IO String
liftM Just getLine
有類型IO (Maybe String)
MaybeT ma
構造函數需要m (Maybe a)
類型的值m (Maybe a)
,其中m = IO
, a = String
。 可能最難分析的步驟是第2步。但實際上,如果你提醒自己類型的liftIO :: IO a -> ma
和lift :: Monad m => ma -> tma
這很容易。 因此,所有工作都是通過類型推斷完成的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.