mardi 22 janvier 2019

Carry STRef implicitly in an environment during computation

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 foralls).

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