I'm trying to represent a set of productions of a grammar in OCaml and existential types would be very useful to model the semantic actions of grammar rules. I've been looking into Menhir source code and existential types are also used to model the semantic actions. Consider the following:
type 'a 'b production = { name: string; rule: 'a expr; action: 'a -> 'b }
A production has a name, a rule that returns 'a
and an action that receives 'a
and returns 'b
. The problem with simple parametric types is that a list of productions could not be polymorphic at all, so I can have an action of string_of_float
and another of string_of_int
in the same list; which is a common issue that can be solved with explicit Ɐ
, for example, action could be:
action: 'a 'b. 'a -> 'b
But I also need to have a constraint based on the parameter of expr
, where 'a
of action
unifies to 'a
of expr
, so, as long as we can have inline record constructors in algebraic data types, we should be able to have them in generalized algebraic data types too, right?
type production = (* Ɐ a. b. *)
| Production: { name: string; rule: 'a expr; action: 'a -> 'b } -> production
But this is not a valid form. The solution I found for now is making the rule also have the action type, for example:
type 'a expr =
| Terminal of string * ('a -> 'a)
| Sequence of 'a expr list * ('a list -> 'a)
But this kind of monomorphism is still very restrictive. How can I model existential types inside records with shared constraints, so I could have different return types in different productions where a compile-time checking of application is still possible (via exhaustive pattern-matching)?
Using the same logic, If I'd like to have a list of functions from 'a 'b. -> 'a -> 'b
, would I need to use an existential type constructor to do that (like in [string_of_int; string_of_float]
)?
I believe I need some sort of kind restriction (they both are * -> *
), but I came from Haskell and still didn't figure out how to do that in OCaml.