如何使用带有Monad变换器的ST Monad

6

Haskell的transformers库提供了MonadIO类和liftIO函数,用于在monad transformer stack中提升IO操作。但我发现对于ST monad,任何monad transformer库中都没有相应实现。这个缺失有什么原因?我如何将ST monad与例如MaybeTReaderT一起使用?


相关包: https://hackage.haskell.org/package/STMonadTrans - sjakobi
我不知道是否有任何类似 liftIO 那样可以神奇地处理变换器堆栈的 liftST。但是,如果没有这样的魔法,我们仍然可以使用 lift 进行工作。我们需要对堆栈中的每个变换器应用一次 lift,因此它不太方便,但应该可以工作。 - chi
2
liftST会有什么类型?ST上的幻影类型变量似乎会让事情变得困难。 - amalloy
@amalloy有趣,但我想知道是否真的如此。我尝试了 lift (newSTRef "hello" >>= \r -> readSTRef r),GHCi将其类型化为MonadTrans t => t (ST s) [Char]。只要我们在基础monad ST s 上使用一组monad变换器堆栈,并且在所有地方都使用相同的s,我认为单步提升没有明显的问题。关于更通用的liftST,也许可以是liftST :: MonadST s m => ST s a -> m a(请注意约束中的s)? - chi
1
https://www.reddit.com/r/haskell/comments/eh2obw/something_like_stref_but_usable_in_a_monad/fcgmji9/ - Joseph Sible-Reinstate Monica
1个回答

2

我尝试了一下,看起来如果你只想使用 ST 作为基本 monad,以下内容可能已足够与“通常”的 transformers 配合使用。

{-# 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)

您可能会发现,有时需要将类型应用于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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接