簡體   English   中英

遞歸liftIO

[英]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左手側是liftIOMaybeT m單子,而liftIO右手邊是liftIOm單子。

因此,這種簡單的定義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 @ashow @b是不同的函數。

對某些專業化進行簡單的等式推理和重寫定義可以幫助您。 MonadIO基本案例是IO MaybeT是monad變換器,所以讓我們在一些簡單的例子中結合MaybeTIO

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)
  1. getLine具有類型IO String
  2. liftM Just getLine有類型IO (Maybe String)
  3. MaybeT ma構造函數需要m (Maybe a)類型的值m (Maybe a) ,其中m = IOa = String

可能最難分析的步驟是第2步。但實際上,如果你提醒自己類型的liftIO :: IO a -> malift :: Monad m => ma -> tma這很容易。 因此,所有工作都是通過類型推斷完成的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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