Howdy! I’m a relative OCaml newcomer, and I’m writing a compiler in it.
My AST data type has the following interesting problem: there are some nodes in the AST that can appear under several sorts of circumstances but which are not valid in all of them.
For example, in this language, a variable definition can appear both at the top level and within a function, but a function definition cannot appear within a function.
However, currently, I have a single variant type for definitions, so the AST could encode an invalid situation where a function definition appeared inside a function definition.
My parser just returns a list of definitions at the top level, and a function definition contains a list of statements, some of which are definitions. This doesn’t allow the AST type to “know” that only certain kinds of definitions can validly appear inside a function.
Technically this isn’t an awful problem, my parser after all doesn’t generate the invalid cases. However, I’d prefer to have the type system itself assure the invalid case couldn’t be accidentally expressed. That is, I’d like to have the type system give stronger assurances that if a parse tree can be expressed, it is valid.
One ugly way to do this would be to break up the definition type into its constituent variants, create two new variants, one at the top level and one at the function level, that will only wrap the kinds of definitions allowed at that level, so one would only allow function definitions in the top level wrapper type. But this seems to be quite ugly looking in practice.
This is only one example, as the problem seems to have crept in several times.
Is there a good or even pretty pattern for doing such things within OCaml’s type system?
I hope this isn’t too obscure without my having given a detailed example from my code. If necessary, I can.