Hello there,
I’m in the midst of figuring out how type checking and inference can be implemented. Everything so far is based on the OutsideIn(X) paper (which is a wonderful paper by the way!) and two talks by Simon Peyton Jones (the “100 Types” and type inference as constraint solving).
This wall of text boils down to a profane question to which I probably guessed the answer already, but I want to be sure …
The language I’m toying around with is a “first simple fragment”, it contains application and lambda abstraction as well as top-level definitions which can be type annotated or not. This sets the local-let aside. (Eventually I want to experiment with and understand records and rows, so I try to reduce the friction and keep it simple.)
On the typing side nothing special as well, just type classes are added on top of everything which is needed for the syntax.
Now, in the talks I’ve mentioned the process of elaboration (that is, adding type and type class dictionary arguments into the AST) happens while generating the constraints. Jones mentions that there are details that this is not actually the case for the hole program – and I guess its these details I stumble upon.
The underlying question is: In which sequence happens typing and elaboration when several top-level definitions exist?
Considering typing alone for a moment, as far as I can see, the OutsideIn paper does one at a time – when one is checked and inferred I’m allowed to use it as part of the context, hence I would probably implement this with some dependency tree (not graph! no (mutually) recursive definitions) to determine the order. When it is typed already, I have all the info to elaborate.
Consider for example something like this in Haskell:
g :: Int -> [Char]
g x = f x ++ "<END>"
f x = "<START>" ++ show x
I would need to start with f
, then instantiate its polymorphic type and elaborate f x
to f @a d x
.
While this is probably just the answer, I still don’t want to believe that I cannot just go the way of the big picture: constraints and elaboration for everything! Simplify everything! Done! ; ). Hence I’m here asking:
(1) Before elaboration the type of the identifier which gets expanded has to be known, either because it was given via annotation or the inference actually has to happen for this identifier first. There is no magic I’m overseeing here, I can only elaborate after type inference if the type is not given, not while constraints are generated?
(2) Constraints are not generated for the hole program (or say: module) but rather a dependency tree is created first and this tree determines an order of type inference and elaboration?
(3) Does each top-level definition get its own typing pass? Or are several independent ones are put together?
(4) I guess this is then Pandora’s box: What about (mutual) recursive definitions?
Thanks in advance,
aphorisme