I have an AST which describes both pure and impure computations, with the impure computations keeping track of state at the type level. I'm using a HOAS representation; the relevant constructors of the AST GADT are as follows:
data AST a where
Lam :: (AST a -> AST b) -> AST (a -> b)
(:$) :: AST (a -> b) -> AST a -> AST b
Return :: AST (a -> (a := i) i)
Bind :: AST ( (a := j) i -> ( a -> q j ) -> q i )
-- then some stateful operations
-- for instance if the indexed state keeps track of a CD player tray:
-- OpenCDPlayer :: AST ( ( () := 'Open ) 'Closed )
-- CloseCDPlayer :: AST ( ( () := 'Closed ) 'Open )
Here (a := j) i
is McBride's AtKey
type, which consists of a type annotated with beginning and end states (i
and j
, respectively). Bind
corresponds to McBride's "angelic bind", which boils down to the provision of a function
bind :: AST ( (a := j) i ) -> Codensity AST (a := j) i
where Codensity
is the indexed version of the usual codensity transformation.
My question is about how to implement evaluation in this situation. Usually, one would have an evaluation function of the form:
eval :: AST a -> a
However in this case I am at a loss as to what type give to the evaluation function, as when state-annotated types are involved I think evaluation should be formulated as having a type close to the following:
evalM :: Codensity AST (a := j) i -> Codensity ((->) St) (a := j) i
where St
allows us to keep track of state at the value-level, so that the right-hand side is an indexed state monad (Codensity ((->) s) ~ State s
).
This type signature for evalM
seems rather intuitive, as it describes a natural transformation of indexed monads, and seems to be the core of the task at hand.
Question: how can I formulate and implement evaluation in this context? What type should it have, and how would I implement the evalM
function above?