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 effect that gives you access to everything in the primitive 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.
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).
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.
yeah, this is probably what I am looking for. The only annoying boilerplate here is the liftST.
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
The Bluefin introduction has example3 which uses a state reference and an exception and ends up in pure code, not IO.
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
Let me know if you have any more questions. You’re welcome to message me here, by opening a new issue on the Bluefin repo or, if necessary, by email.
Yes exactly, it’s the ST-trick but the es type variable pokes out so you can use it for other effects.
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!