简体   繁体   English

使用 monadtransformer 复制 liftIO 的概念

[英]Replicating the concept of liftIO with a monadtransformer

I have trouble even explaining what I'm trying to do, please bear with me.我什至无法解释我要做什么,请多多包涵。 I haven't really grasped type theory, or how to use forall so that is probably the reason why wording (and implementing) this is difficult for me.我还没有真正掌握类型理论,或者如何使用forall ,所以这可能是为什么措辞(和实现)这对我来说很困难的原因。 So basically I'm trying to replicate the behaviour of MonadIO with a MonadTransformer.所以基本上我试图用 MonadTransformer 复制 MonadIO 的行为。 MonadIO is defined as MonadIO 定义为

class (Monad m) => MonadIO m where
    -- | Lift a computation from the 'IO' monad.
    liftIO :: IO a -> m a

-- | @since 4.9.0.0
instance MonadIO IO where
    liftIO = id

This code compiles (I haven't included the Applicative and Functor instances here though, but I can):这段代码可以编译(虽然我没有在这里包含 Applicative 和 Functor 实例,但我可以):

newtype Debugable m r = Debugable { runDebugable :: (DebugState -> m (DebugState, r)) }

instance MonadTrans (Debugable)  where
  lift action = Debugable $ \d -> do
    r <- action
    return (d, r)

class (Monad i, Monad m) => MonadDebug i m where
  liftDebug :: Debugable i a -> m a


instance (Monad m) => MonadDebug m (Debugable m) where
  liftDebug = id

I want to write a method like this, that should work in any MonadTransformer above MonadDebug in the transformer stack:我想写一个这样的方法,它应该在变压器堆栈中 MonadDebug 之上的任何 MonadTransformer 中工作:

debug :: (MonadDebug i m) => String -> m ()
debug msg = liftDebug $ doDebug msg

(doDebug operates in the Debugable monad) (doDebug 在 Debugable monad 中运行)

Of course this does not compile because the type variable i cannot be deduced (or something like that)... I tried a few things, but I don't really understand what the debug messages mean either.当然这不会编译,因为i无法推断出类型变量(或类似的东西)......我尝试了一些东西,但我也不真正理解调试消息的含义。 I don't even know if a generalized lifting of a MonadTransformer similar to liftIO is possible... Is it?我什至不知道是否可以对类似于 liftIO 的 MonadTransformer 进行通用提升......是吗?

I suggest changing your goal slightly.我建议稍微改变你的目标。

The idiomatic way to make this class is to expose all of the base transformer's operations as class methods, rather than exposing a lifting transformation.制作此 class 的惯用方法是将所有基本变压器的操作公开为 class 方法,而不是公开提升变换。 Like this:像这样:

class Monad m => MonadDebugable m where
    debug :: (DebugState -> (DebugState, r)) -> m r

instance Monad m => MonadDebugable (Debugable m) where
    debug f = Debugable (pure . f)

instance MonadDebugable m => MonadDebugable (ReaderT r m) where
    debug = lift . debug
-- and a dozen other instances for other transformers

Then you can offer derived operations, like, say,然后你可以提供派生操作,比如,

observeCurrentState :: MonadDebugable m => m DebugState
observeCurrentState = debug (\s -> (s, s))

recordState :: MonadDebugable m => DebugState -> m ()
recordState s = debug (\_ -> (s, ()))

debugMsg :: MonadDebugable m => String -> m ()
debugMsg msg = debug (\s -> (s { messages = msg : messages s }, ())) -- or whatever

Then, in all your client code, where you would originally have written liftDebug foo , you simply write foo (but using operations like those above that are polymorphic over which exact monad stack you're using at the moment).然后,在您最初编写liftDebug foo的所有客户端代码中,您只需编写foo (但使用像上面那样的操作,这些操作对于您目前正在使用的确切 monad 堆栈是多态的)。

Of course, the obvious follow-up question is: what's the deal with MonadIO , then?当然,显而易见的后续问题是:那么MonadIO是怎么回事? Why doesn't IO follow this pattern?为什么IO不遵循这种模式? The answer there is that IO just has too many primitive operations to write a sensible class -- indeed, with the FFI, it's even possible for libraries and users to add new primitives.答案是IO只是有太多的原始操作来编写合理的 class —— 实际上,使用 FFI,库和用户甚至可以添加新的原始操作。 So we have to go with the slightly less desirable solution there, where we must annotate all our primitive calls with liftIO if we're not exactly in IO already.因此,我们必须在 go 中使用稍微不太理想的解决方案,如果我们不完全在IO中,我们必须使用liftIO注释所有原始调用。 Skipping the annotations, like above, would be nicer, but we just can't have it.像上面那样跳过注释会更好,但我们就是做不到。

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

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