Search code examples
haskellexistential-typegadt

Hiding a State monad's s type parameter


I'm trying to hide the type parameter of a State monad in a new type, but I'm having a hard time unifying the existentially qualified s with the g to be provided for evalFoo. I've tried with ExistentialQuantification, GADTs, and RankNTypes, but have an admittedly very poor understanding of how these extensions work.

How would the idiomatic Haskell way to accomplish this look? Thanks!

{-# LANGUAGE GADTs #-}

import Control.Monad.State
import System.Random

data Foo a where
  Foo :: RandomGen s => State s a -> Foo a

evalFoo :: RandomGen g => Foo a -> g -> a
evalFoo (Foo m) g = evalState m g

The goal is to achieve something like this, but to able to supply any instance of RandomGen:

myRNG :: Foo Double
myRNG = Foo $ do
  u <- state random
  return u

Prelude> evalFoo myRNG (mkStdGen 123)
0.7804356004944119

Solution

  • Existential quantification in the type of the Foo constructor would mean that for every value of type Foo, there is some instance of RandomGen that it uses as its state. You want the opposite, though: you want that given any value foo :: Foo, and any instance g of RandomGen, you can use g as the state of the computation encapsulated by foo.

    So let's write that instead:

    {-# LANGUAGE Rank2Types #-}
    
    import Control.Monad.State
    import System.Random
    
    newtype Foo a = MkFoo{ unFoo :: forall g. (RandomGen g) => State g a }
    
    evalFoo :: RandomGen g => Foo a -> g -> a
    evalFoo = evalState . unFoo
    

    This can be used as expected:

    myRNG :: Foo Double
    myRNG = MkFoo $ do
        u <- state random
        return u
    

    giving

    *Main> evalFoo myRNG (mkStdGen 123)
    0.43927189736460226
    

    Yeah, not quite 0.78 ;)