Skip to content

Instantly share code, notes, and snippets.

@igrep
Last active September 15, 2019 10:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igrep/9c5a19d820d9bc022cb286a3b90cbb4e to your computer and use it in GitHub Desktop.
Save igrep/9c5a19d820d9bc022cb286a3b90cbb4e to your computer and use it in GitHub Desktop.
Simulate ST Monad without "impure" things
#!/bin/env stack
{-
stack script --resolver=lts-14.6
--package=mtl
--package=containers
-}
{-# OPTIONS -fdefer-type-errors #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
import Data.Dynamic
import qualified Data.IntMap.Strict as IM
import Data.Maybe
import Control.Exception
import Control.Monad.State.Strict
data STState = STState { lastId :: !Int, refs :: !(IM.IntMap Dynamic) } deriving Show
newtype STRef s a = STRef { unSTRef :: Int }
newtype ST s a = ST { unST :: State STState a }
deriving (Functor, Applicative, Monad)
runST :: (forall s. ST s a) -> a
runST = (`evalState` (STState 0 IM.empty)) . unST
newSTRef :: Typeable a => a -> ST s (STRef s a)
newSTRef x = do
state <- ST get
let curId = lastId state
curRefs = refs state
ST $ put $! STState (curId + 1) $ IM.insert curId (toDyn x) curRefs
return $ STRef curId
readSTRef :: Typeable a => STRef s a -> ST s a
readSTRef (STRef i) =
ST $ gets
( fromMaybe (error $ "Assertion failure: Ref#" ++ show i ++ " is has unexpected type!")
. fromDynamic
. fromMaybe (error $ "Assertion failure: No ref#" ++ show i)
. IM.lookup i
. refs
)
main :: IO ()
main = do
print $ runST $ do
ref1 <- newSTRef 'a'
ref2 <- newSTRef True
(,) <$> readSTRef ref1 <*> readSTRef ref2
evaluate $ runST $ newSTRef True -- This line should cause a type error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment