如何将 ST Monad 与 monad 变压器一起使用

Ton*_*bda 6 monads haskell functional-programming monad-transformers

haskelltransformers库提供MonadIO类并liftIO在 monad 变压器堆栈中提升 IO 操作。在我看来,ST monad 也可以做同样的事情,但我在任何 monad 转换器库中都找不到它。这个遗漏有原因吗?如何将 ST monad 与 eg, MaybeTor 一起使用ReaderT

K. *_*uhr 2

我试了一下,看起来如果您只想用作ST基本 monad,那么以下内容可能足以与“常用”变压器一起使用。

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}

module TransST
  (liftST, MonadST)
  where

import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Writer
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.RWS
import Control.Monad.Trans.Except
import Control.Monad.ST

class Monad m => MonadST s m where
  liftST :: ST s a -> m a
instance MonadST s (ST s) where
  liftST act = act
instance (MonadST s m) => MonadST s (ReaderT r m) where
  liftST act = lift (liftST act)
instance (MonadST s m) => MonadST s (StateT st m) where
  liftST act = lift (liftST act)
instance (Monoid w, MonadST s m) => MonadST s (WriterT w m) where
  liftST act = lift (liftST act)
instance (Monoid w, MonadST s m) => MonadST s (RWST r w s m) where
  liftST act = lift (liftST act)
instance (MonadST s m) => MonadST s (MaybeT m) where
  liftST act = lift (liftST act)
instance (MonadST s m) => MonadST s (ExceptT e m) where
  liftST act = lift (liftST act)
Run Code Online (Sandbox Code Playgroud)

您可能会发现有时需要添加类型应用程序liftST或其他函数来解决 ST monad 参数的不明确使用,如以下测试用例所示:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

import TransST

import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Control.Monad.Except
import Control.Monad.Trans.Maybe
import Control.Monad.ST
import Data.STRef
import Data.Char

type M s = ReaderT Int (WriterT [String] (StateT Char (ExceptT String (MaybeT (ST s)))))

runM :: (forall s. M s a) -> Int -> Char -> Maybe (Either String ((a, [String]), Char))
runM act r s = runST $ runMaybeT $ runExceptT
  $ flip runStateT s $ runWriterT $ runReaderT act r

someSTOperation :: (MonadST s m) => Int -> m (STRef s Int)
someSTOperation x = liftST (newSTRef x)

test :: Maybe (Either String ((Int, [String]), Char))
test = runM act 5 'a'
  where
    act :: forall s. M s Int
    act = do
      tell ["starting"]
      x <- gets ord
      s <- someSTOperation @s x  -- needs a type annotation
      r <- ask
      liftST (writeSTRef s (x + r))
      put . chr =<< liftST (readSTRef s)
      c <- get
      return (ord c)
Run Code Online (Sandbox Code Playgroud)