Как раскошелиться внутри монадного трансформатора - PullRequest
9 голосов
/ 06 марта 2012

Рассмотрим стек монадных трансформаторов, скажем,

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
...
newtype J = J { runJ :: ErrorT Foo (StateT Bar IO) a } deriving (Applicative, Functor, etc)

И некоторые функции в J:

peekNextQuux :: J Quux
peekNextQuux = ...

withJ :: J a -> IO (Either Foo a)
withJ = ...

Тогда я оказался внутри J контекста. Я могу написать

f = withJ $ peekNextQuux >>= liftIO . print

Теперь я хочу посмотреть и напечатать quuxes внутри отдельного потока внутри J context

g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print

Что, очевидно, не сработает. Я думаю, что есть какой-то способ решить такую ​​простую проблему, просто не могу понять это.

Ответы [ 2 ]

9 голосов
/ 06 марта 2012

Я не уверен, что это то, что вам нужно, но звучит так, будто вы ищете функцию

forkJ :: J () -> J ThreadId

, которая похожа на forkIO, но вместо этого работает в J-контексте.Вообще говоря, все точки dflemstr действительны.Есть много нерешенных вопросов об управлении состоянием из-за чистоты Haskell.

Однако, если вы хотите немного перестроить свою логику, один вариант, который может работать для вас (если все, что вы ищете, этоотдельный поток с доступом к исходному состоянию при выдаче форка) - это pakcage lifted-base , который зависит от монад-контроля.Это, по сути, даст вам функцию forkJ, указанную выше, до тех пор, пока у вас есть IO в нижней части стека трансформаторов.

Теперь, если вы хотите, чтобы 2 потока взаимодействовали с состоянием, таким образом, чтобы ошибки возникали вchild передаются в основной поток как часть механизма ErrorT, это просто невозможно (как объяснил dflemstr).Однако вы можете установить канал связи между двумя потоками, используя конструкцию из семейства модулей Control.Concurrent.Один из следующих модулей может иметь то, что вам нужно:

Control.Concurrent.Chan
Control.Concurrent.MVar
Control.Concurrent.STM
8 голосов
/ 06 марта 2012

Как вы ожидаете, что это будет работать? Отдельный поток должен иметь доступ к некоторому состоянию и некоторой обработке ошибок, потому что J упаковывает StateT и ErrorT. Как поток должен получить доступ к этому? Когда состояние обновляется в новом потоке, должно ли оно быть изменено и в старом потоке? Когда новый поток генерирует исключение, должен ли старый поток остановиться?

Это не может работать, потому что StateT и ErrorT являются чисто монадными преобразователями, поэтому описанные мной варианты поведения невозможно реализовать. Вы должны явно передать состояние новому потоку и запустить новую монаду состояний, чтобы оно работало:

g = withJ . ... $ do
  state <- get
  liftIO . forkIO $ do
    flip execStateT state . forever $ peekNextQuux >>= liftIO . print
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...