Я не знаю, как с научной точки зрения сформулировать этот вопрос, поэтому я просто покажу вам пример.
Я использую состояние в преобразователе StateT
. Базовым является IO
. Внутри операции StateT IO
мне нужно использовать alloca
. Однако я не могу поднять alloca
до StateT IO
, потому что он ожидает аргумент типа (Ptr a -> IO a)
, а мне нужно, чтобы он работал с аргументом (Ptr a -> StateT IO MyState a)
.
(Однако это общий вопрос о преобразователях монад, а не о конкретных IO
, StateT
или alloca
.)
Я придумал следующее рабочее решение:
-- 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
.
Есть лучший способ сделать это?
alloca
недостаточно общий, чтобы разрешить то, что вы хотите сделать. Он принимает функцию, возвращающуюIO
, вместо функции, возвращающей любую монаду, в которой может быть поднят IO (т. е.MonadIO
). В этом конкретном случае вы можете избежать распаковки, если реализуетеallocaS
, используяmalloc/free
вместоalloca
(если вы это сделаете, следите за исключениями). В общем случае я не вижу способа сделать это без более общей формыalloca
. Возможно, вы сможете сначала написать такой общийalloca
, перейдя на довольно низкий уровень и используя примитивы GHC. - person chi   schedule 03.08.2014