将函数及其参数提升到不同的单一上下文 [英] Lift a function and its argument to a different monadic context

查看:86
本文介绍了将函数及其参数提升到不同的单一上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不确定如何科学准确地提出这个问题,所以我只是要向你展示一个例子。

I am not sure how to formulate this question scientifically exact, so I am just going to show you an example.

我在 StateT 变压器中使用状态。底层是 IO 。在 StateT IO 操作中,我需要使用 alloca 。但是,我不能提取 alloca StateT IO ,因为它期望类型为的参数。 (Ptr a - > IO a),而我要求它使用参数(Ptr a - > StateT IO MyState a)

I am using state in a StateT transformer. Underlying is IO. Inside the StateT IO operation I need to use alloca. However, I can't lift alloca to StateT IO because it expects an argument of type (Ptr a -> IO a) while I require it to work with an argument of (Ptr a -> StateT IO MyState a).

(然而,这是一个关于monad转换器的通用问题,而不是特定于 IO StateT alloca 。)

(However, this is a generic question about monad transformers rather than specific to IO, StateT or alloca.)

解决方案:

I came up with the following, working solution:

-- for reference
-- alloca :: (Storable a) => (Ptr a -> IO b) -> IO b

allocaS :: (Storable a) => (Ptr a -> StateT s IO b) -> StateT s IO b
allocaS f = do
  state <- get
  (res, st) <- liftIO $ alloca $ \ptr -> (runStateT (f ptr) state)
  put st
  return res

然而,我认为我应该不得不重构和重构 StateT 动作,以便将它与 alloca 。此外,我已经多次看到这种模式的一些变化,并不总是像 StateT 那样简单和安全。

However, it seems wrong to me that I should have to de- and reconstruct the StateT action in order to use it with alloca. Also, I have seen this pattern in some variations more than once and it's not always as simple and safe as here with StateT.

有没有更好的方法来做到这一点?

Is there a better way to do this?

推荐答案

这可以使用 MonadBaseControl monad-control 中的code> ,已设计正是为此:

This can be accomplished using MonadBaseControl in monad-control, which has been devised exactly for this purpose:

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad
import Control.Monad.Trans.Control
import qualified Foreign.Ptr as F
import qualified Foreign.Marshal.Alloc as F
import qualified Foreign.Storable as F

alloca :: (MonadBaseControl IO m, F.Storable a) => (F.Ptr a -> m b) -> m b 
alloca f = control $ \runInIO -> F.alloca (runInIO . f)

这个增强版本的 alloca 可以用于任何实现 MonadBaseControl 的基于 IO 的monad堆栈,包括 StateT s IO

This enhanced version of alloca can be used with any monad stack based on IO that implements MonadBaseControl, including StateT s IO.

MonadBaseControl 的实例允许其monadic值为编码在基本monad中(这里 IO ),传递给基本monad中的函数(如 F.alloca )然后重新构建它们。

Instances of MonadBaseControl allow their monadic values to be encoded in the base monad (here IO), passed to a function in the base monad (like F.alloca) and then reconstruct them back.

另请参阅 MonadBaseControl的用途是什么?

套餐提升基地包含许多标准的 IO 函数提升到 MonadBaseControl IO ,但是 alloca 不在(其中)。

Package lifted-base contains many of the standard IO functions lifted to MonadBaseControl IO, but alloca isn't (yet) among them.

这篇关于将函数及其参数提升到不同的单一上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆