# Transformer version of the ST monad?

**URL:** <https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623>\
**Category:** Learn\
**Created:** [January 30, 2026, 2:16am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623 "2026-01-30T02:16:33Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Agnishom](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/agnishom/32/2108_2.png) [@Agnishom](https://discourse.haskell.org/u/Agnishom)\
**Post date:** [January 30, 2026, 2:16am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/1 "2026-01-30T02:16:33Z")

</div>

I want to stack the ST Monad together with other conveniences like `MonadError`, and so on.

It seems that there are packages like [STT](https://hackage.haskell.org/package/stt), but they are widely used, or their use is discouraged.

What is the best way to write a convenient computation with other effects, like `MonadError` and so on, such that `runST` can still be run on it later?

---

<div class="post-metadata">

**Author:** ![arybczak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/arybczak/32/2527_2.png) [@arybczak](https://discourse.haskell.org/u/arybczak)\
**Post date:** [January 30, 2026, 3:20am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/2 "2026-01-30T03:20:49Z")

</div>

Best way is subjective, but if you don’t mind ending up in IO, you can use effectful - apart from standard stuff like Error, State etc. it has a [Prim](https://hackage-content.haskell.org/package/effectful-core-2.6.1.0/docs/Effectful-Prim.html) effect that gives you access to everything in the [primitive](https://hackage.haskell.org/package/primitive-0.9.0.0) library, which is an extension of ST pretty much.

Alternatively you can use ExceptT on top of ST, but then you’ll have to write `lift` a lot.

---

<div class="post-metadata">

**Author:** ![jackdk](https://avatars.discourse-cdn.com/v4/letter/j/9f8e36/32.png) [@jackdk](https://discourse.haskell.org/u/jackdk)\
**Post date:** [January 30, 2026, 3:55am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/3 "2026-01-30T03:55:11Z")

</div>

That `primitive` library also has a `MonadPrim` class with instances for `ST`.

---

<div class="post-metadata">

**Author:** ![Agnishom](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/agnishom/32/2108_2.png) [@Agnishom](https://discourse.haskell.org/u/Agnishom)\
**Post date:** [January 30, 2026, 5:09am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/4 "2026-01-30T05:09:04Z")

</div>

This is an interesting option, but I would prefer not to end up in `IO`. This is why I am looking to combine something like `runST` with other effects (like throwing errors, for example).

---

<div class="post-metadata">

**Author:** ![Agnishom](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/agnishom/32/2108_2.png) [@Agnishom](https://discourse.haskell.org/u/Agnishom)\
**Post date:** [January 30, 2026, 5:34am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/5 "2026-01-30T05:34:29Z")

</div>

Can we define an effect in `effectful` which utilizes the same trick as `ST` which allows the result to be pure? Perhaps something like this:

```haskell
data EffectST s :: Effect where
  LiftST :: ST s a -> EffectST s m a

runEffectST :: (forall s. Eff (EffectST s : es) a) -> Eff es a

```

---

<div class="post-metadata">

**Author:** ![ashokkimmel](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ashokkimmel/32/5111_2.png) [@ashokkimmel](https://discourse.haskell.org/u/ashokkimmel)\
**Post date:** [January 30, 2026, 5:40am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/6 "2026-01-30T05:40:45Z")

</div>

I don’t think you can, as `STTrans []` would have to duplicate the `STRef` in memory so that the list refrences wouldn’t be mutated by each other.

---

<div class="post-metadata">

**Author:** ![jmcarthur](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jmcarthur/32/3283_2.png) [@jmcarthur](https://discourse.haskell.org/u/jmcarthur)\
**Post date:** [January 30, 2026, 5:46am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/7 "2026-01-30T05:46:04Z")

</div>

I would recommend transforming `ST` instead of looking for a monad transformer that gives you `ST` effects. The latter would not behave the way you want for all monads.

---

<div class="post-metadata">

**Author:** ![Agnishom](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/agnishom/32/2108_2.png) [@Agnishom](https://discourse.haskell.org/u/Agnishom)\
**Post date:** [January 30, 2026, 6:43am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/8 "2026-01-30T06:43:46Z")

</div>

yeah, this is probably what I am looking for. The only annoying boilerplate here is the `liftST`.

```haskell

type BankM s a = ExceptT Text (ReaderT (STRef s Int) (ST s)) a

liftST :: ST s a -> BankM s a
liftST = lift . lift

withdraw :: Int -> BankM s ()
withdraw amount = do
  ref <- ask
  currentBalance <- liftST $ readSTRef ref
  if amount > currentBalance
    then
      throwError $ "Insufficient funds! Balance is: " <> Text.pack (show currentBalance)
    else
      liftST $ writeSTRef ref (currentBalance - amount)

runBank :: Int -> (forall s. BankM s a) -> Either Text a
runBank initialBalance action = runST $ do
  ref <- newSTRef initialBalance
  runReaderT (runExceptT action) ref

getBalance :: BankM s Int
getBalance = do
  ref <- ask
  liftST $ readSTRef ref

pureWithdraw :: Int -> Either Text Int
pureWithdraw amount = runBank 1000 $ do
  withdraw amount
  getBalance

```

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [January 30, 2026, 6:57am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/9 "2026-01-30T06:57:31Z")

</div>

[Bluefin](https://hackage-content.haskell.org/package/bluefin-0.2.6.0/docs/Bluefin.html) basically _is_ “`ST`-with-more-effects”.

The Bluefin introduction has [`example3`](https://hackage-content.haskell.org/package/bluefin-0.2.6.0/docs/Bluefin.html#g:21) which uses a state reference and an exception and ends up in pure code, not `IO`.

```haskell
example3 :: Int -> Either String Int
example3 n = runPureEff $
  try $ \ex -> do
    evalState 0 $ \total -> do
      for_ [1..n] $ \i -> do
         soFar <- get total
         when (soFar > 20) $ do
           throw ex ("Became too big: " ++ show soFar)
         put total (soFar + i)

      get total

```

Check out

- [`Bluefin.State`](https://hackage-content.haskell.org/package/bluefin-0.2.6.0/docs/Bluefin-State.html), and
- [`Bluefin.StateSource`](https://hackage-content.haskell.org/package/bluefin-0.2.6.0/docs/Bluefin-StateSource.html)

Let me know if you have any more questions. You’re welcome to message me here, by opening [a new issue](https://github.com/tomjaguarpaw/bluefin/issues/new) on the Bluefin repo or, if necessary, [by email](http://web.jaguarpaw.co.uk/~tom/contact/).

---

<div class="post-metadata">

**Author:** ![Agnishom](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/agnishom/32/2108_2.png) [@Agnishom](https://discourse.haskell.org/u/Agnishom)\
**Post date:** [January 30, 2026, 7:10am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/10 "2026-01-30T07:10:31Z")

</div>

Thanks for the pointer. Indeed, looking at the type signature

```haskell
runState ::
  forall s (es :: Effects) a. s
  -> (forall (e :: Effects). State s e -> Eff (e :& es) a)
  -> Eff es (a, s)

```

it seems that it also has a similar trick going on.

Can one use something like this [ST based mutable Hashtable](https://hackage.haskell.org/package/hashtables) in Bluefin?

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [January 30, 2026, 7:22am UTC](https://discourse.haskell.org/t/transformer-version-of-the-st-monad/13623/11 "2026-01-30T07:22:44Z")

</div>

> [@Agnishom](#):
>
> it seems that it also has a similar trick going on.

Yes exactly, it’s the `ST`-trick but the `es` type variable pokes out so you can use it for _other_ effects.

> [@Agnishom](#):
>
> Can one use something like this [ST based mutable Hashtable](https://hackage.haskell.org/package/hashtables) in Bluefin?

EDIT: yes, in a work in progress branch. For example, this mixes `ST` and `IO`. Of course you want to _not_ use `IO`. That’s fine, it could be any other effects, or none. I just wanted to be able to print stuff out!

> <https://github.com/tomjaguarpaw/bluefin/commit/95471ed8947815becf546cec98e6bf564a45534d#diff-894d331144d8e3c7c05f8802696248a83f75d119a77fa2c8e5e17375aa93e7b3>

Please do [file a new issue on Bluefin](https://github.com/tomjaguarpaw/bluefin/issues/new) and encourage me to merge this if you’re interested in using it.

````haskell
-- ghci> example
-- 0
-- 10
-- <HashTable>
-- [("hello",42),("bye",1099)]
-- No more ST
example :: IO ()
example = runEff_ $ \io -> do
  do
    runST $ \st -> do
      ref <- effST st (STRef.newSTRef @Int 0)
      v1 <- effST st (STRef.readSTRef ref)
      effIO io (print v1)
      effST st (STRef.modifySTRef ref (+ 10))
      v2 <- effST st (STRef.readSTRef ref)
      effIO io (print v2)
      ht <- effST st (HT.new @_ @String @Int)
      effST st (HT.insert ht "hello" 42)
      effST st (HT.insert ht "bye" 99)
      effIO io (print ht)
      effST st (HT.mutate ht "bye" $ \m -> (fmap (+ 1000) m, ()))
      htList <- effST st (toList ht)
      effIO io (print htList)
  effIO io (putStrLn "No more ST")```

````

* * *

EDIT: Old:

Yes, but I don’t have a generic way of running `ST` actions in Bluefin yet, so I think the simplest way is just to wrap the [`IO` version](https://hackage.haskell.org/package/hashtables-1.4.2/docs/Data-HashTable-IO.html#t:BasicHashTable) like

```haskell
newtype BfHashTable k v es =
  MkBfHashTable (IO.IOHashTable HashTable k v)

```

```haskell
new ::
  (forall e. BfHashTable k v e -> Eff (e :& es) r) ->
  Eff es r
new = ... IO.new

```

```haskell
lookup ::
  (Eq k, Hashable k, e :> es) =>
  BfHashTable k v e ->
  k ->
  Eff (Maybe v) 
lookup = ... IO.lookup

```

You can follow the recipe that is used by `State` to wrap `IORef` and get an equivalent of `STRef`: [bluefin/bluefin-internal/src/Bluefin/Internal.hs at 21994b19fd90db550353da4132db7580ea8befec · tomjaguarpaw/bluefin · GitHub](https://github.com/tomjaguarpaw/bluefin/blob/21994b19fd90db550353da4132db7580ea8befec/bluefin-internal/src/Bluefin/Internal.hs#L355)

This is all a bit complicated to implement but I’m happy to help, and once it’s implemented it should be as easy as `ST` to use.
