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
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 ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With