Асинхронность Хаскелла: Использование различных типов монад в withAsync

#haskell #asynchronous #exception #monad-transformers #stm

Вопрос:

Я пишу трансформатор монады, и для запуска трансформатора мне нужно создать поток ввода-вывода, который записывает элементы в TQueue, к которому имеет доступ основной поток. Однако ни одна из версий withAsync не предоставляет правильную подпись. Мне нужен один с подписью, что-то вроде

 (MonadIO m) => IO a -> (Async a -> m b) -> m b
 

Я попытался поднять асинхронность, но у этого было много ограничений, и как основной, так и порожденный поток находились в одной и той же Монаде.

Альтернативно, с момента Контроля.Параллельный.Async говорит, что withAsync эквивалентен

 withAsync action inner = mask $ restore -> do
    a <- async (restore action)
    restore (inner a) `finally` uninterruptibleCancel a
 

Альтернативным решением была бы версия маски, которая имеет сигнатуру типа, что-то вроде

 mask :: (MonadMask m) => ((forall a n. (MonadMask n) => n a -> n a) -> m b) -> m b
 

Ближе всего к решению я подошел со следующим кодом, который, я почти уверен, не работает, но я не могу быть уверен, так как я не до конца понимаю, как работает маска.

 withAsyncT :: (MonadIO m, MonadMask m) => IO a -> (Async a -> m b) -> m b
withAsyncT action inner = mask $ restore -> do
     a <- liftIO $ asyncWithUnmask (rstr -> rstr action)
     restore (inner a) `finally` (liftIO $ uninterruptibleCancel a)
 

Есть ли разумное решение, чтобы получить такую синхронизацию? Или мне нужно использовать что-то вроде unliftio?