简体   繁体   English

递归liftIO

[英]Recursive liftIO

I've looked at the some instances of MonadTrans, for MaybeT the implementation looks like this: 我已经看过MonadTrans的一些实例,对于MaybeT,实现看起来像这样:

instance MonadTrans MaybeT where
    lift = MaybeT . liftM Just

As I understand the instance for MonadIO is used to do a variable number of lifts from the inner most, a IO monad, directly to the outermost. 据我所知,MonadIO的实例用于从内部进行可变数量的提升,一个IO monad,直接到最外层。 For the MaybeT case it looks like this: 对于MaybeT案例,它看起来像这样:

instance (MonadIO m) => MonadIO (MaybeT m) where
    liftIO = lift . liftIO

What I don't understand is how this recursive function escapes the infinite loop. 我不明白的是这个递归函数如何逃避无限循环。 What is the base case? 什么是基本情况?

Perhaps surprisingly, the definition below is not recursive, even if it looks such. 也许令人惊讶的是,下面的定义不是递归的,即使看起来如此。

instance (MonadIO m) => MonadIO (MaybeT m) where
    liftIO = lift . liftIO

This is because the liftIO on the left hand side is the liftIO for the MaybeT m monad, while the liftIO on the right hand side is the liftIO for the m monad. 这是因为liftIO左手侧是liftIOMaybeT m单子,而liftIO右手边是liftIOm单子。

Hence, this simply defines liftIO in one monad in terms of the liftIO for another monad. 因此,这种简单的定义liftIO在一个单子在方面liftIO另一个单子。 No recursion here. 这里没有递归。

This is similar to eg 这类似于例如

instance (Show a, Show b) => Show (a,b) where
   show (x,y) = "(" ++ show x ++ ", " ++ show y ++ ")"

Above, we define how to print a pair depending on how to print their components. 上面,我们定义如何打印一对,具体取决于如何打印其组件。 It looks recursive, but it is not really such. 它看起来像递归,但事实并非如此。

It could help visualizing this by inserting explicit type arguments, at least mentally: 它可以通过插入显式类型参数来帮助可视化,至少在心理上:

-- pseudo-code
instance (Show a, Show b) => Show (a,b) where
   show @(a,b) (x,y) = 
      "(" ++ show @a x ++ ", " ++ show @b y ++ ")"

Now show @(a,b) , show @a , and show @b are distinct functions. 现在show @(a,b)show @ashow @b是不同的函数。

Simple equational reasoning and rewriting definitions for some specialization can help you. 对某些专业化进行简单的等式推理和重写定义可以帮助您。 Base case for MonadIO is IO . MonadIO基本案例是IO MaybeT is monad transformer, so lets combine MaybeT and IO in some simple example. MaybeT是monad变换器,所以让我们在一些简单的例子中结合MaybeTIO

foo :: MaybeT IO String
foo = liftIO getLine

Now let's rewrite this function definition applying instance implementations from your question step by step. 现在让我们一步一步地重写这个函数定义,从你的问题中应用实例实现。

foo
= liftIO {- for MaybeT -} getLine
= lift (liftIO {- for IO here -} getLine)  -- step 2
= lift (id getLine)
= lift getLine
= MaybeT (liftM Just getLine)
  1. getLine has type IO String getLine具有类型IO String
  2. liftM Just getLine has type IO (Maybe String) liftM Just getLine有类型IO (Maybe String)
  3. MaybeT ma constructor needs value of type m (Maybe a) where m = IO and a = String in our case. MaybeT ma构造函数需要m (Maybe a)类型的值m (Maybe a) ,其中m = IOa = String

Probably hardest step to analyze is step 2. But in reality it's very easy if you remind yourself types of liftIO :: IO a -> ma and lift :: Monad m => ma -> tma . 可能最难分析的步骤是第2步。但实际上,如果你提醒自己类型的liftIO :: IO a -> malift :: Monad m => ma -> tma这很容易。 So all work is done by type inference. 因此,所有工作都是通过类型推断完成的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM