I am working on some bigger computation that requires use of mutable data in some critical moments. I want to avoid IO as much as I can. My model used to constist of ExceptT
over ReaderT
over State
datatype, and now I want to replace State
with mentioned ST
.
To simplify, let's assume I would like to keep single STRef
with an Int
during the whole computation, and let's skip the ExceptT
outer layer. My initial idea was to put STRef s Int
into ReaderT
's environment:
{-#LANGUAGE Rank2Types#-}
{-#LANGUAGE ExistentialQuantification#-}
data Env = Env { supply :: forall s. STRef s Int }
data Comp a = forall s. Comp (ReaderT Env (ST s) a)
And the evaluator:
runComp (Comp c) = runST $ do
s <- newSTRef 0
runReaderT c (Env {supply = s}) -- this is of type `ST s a`
...and it fails because
Couldn't match type ‘s’ with ‘s1’
Which seems to be clear, because I have mixed two separate phantom ST states. However, I have no idea how to bypass it. I have tried adding phantom s
as Comp
and Env
parameter, but the result was the same and the code got uglier (but less suspicious because of lack of these forall
s).
The feature I am trying to achieve here is to make supply
accessible at any time, but not passed explicitly (it does not deserve it). The most comfortable place to store it is in the environment, but I see no way in initializing it.
I know there is such a thing like STT
monad transformer which may help here, but it is not compatible with more ambitious data structures like hashtables (or is it?), so I don't want to use it as long as I cannot freely use classic ST
libraries there.
How to properly design this model? By "properly" I mean not only "to typecheck" but to "to be nice to the rest of the code" and "as flexible as possible".
Aucun commentaire:
Enregistrer un commentaire