Open pchiusano opened 5 years ago
Some options on the table:
@francisdb IMO, The Dark Night Rises is predictable enough; the one we want to improve is Type-Directed Name Resolution.
Current algorithm for TDNR:
In a program with type errors elsewhere and multiple TNDR slots, sticking a blank with a fresh existential type for each slot is way underconstrained and generates unpredictable results. The blanks can cover up the type errors and cause them to resurface elsewhere (or prevent TDNR elsewhere), and when multiple TNDR slots interact (think foo (x + y)
, it's also unpredictable).
Here's a single pass algorithm that I think would be predictable:
f x y z
, where f
is a TDNR slot, we first synthesize x
, y
, and z
. Then we see if any of the possibilities for f
can be applied to these types. If yes, and it's unique, do it. If no, type error.f
, against a type, t
, we do the same - see if any of the possibilities for f
are a subtype of t
and if it's got a unique solution, use it.Notice both rules are totally local and can be done in a single typechecking pass. So long as the argument types are known at the point of the call, that type information will be used to select among the functions f
. A typical usage is like:
foo : Nat -> Nat -> Nat
foo x y =
x + y
Here, the synthesized types of x
and y
(based on the type signature) will be Nat
, so the right +
is selected. Another possibility is like x + 1
, even when x
is of an unknown type, since the 1
literal has type Nat
and that narrows it down to Nat.+
.
Other examples:
f (g x)
, as long as the x
is of known type, that info propagates and is used to select among g
and then f
possibilities.x |> f
also works I think, it will synthesize the type of (|>) x
, and if x : Nat
is known, that refines the function type to say Nat -> ⅇ
, and then checks f
against Nat -> ⅇ
. If f
is a TDNR slot, Rule 2 above then kicks in to refine it.Some drawbacks:
fromSequence [1,2,3]
doesn't work as a short for Set.fromSequence
if there's also like Stream.fromSequence
. The arguments to the function aren't refining its type.Sequence.toSet
, which you could use as toSet
(even if there was also Stream.toSet
) as long as the argument to toSet
was known to be a Sequence
.It reminds me a little bit of multimethods - dispatch is done based on the types of the arguments. Though the Rule 2 makes it a bit more flexible.
Ideas on implementation:
TDNR
monad and just bake Rule 1 and Rule 2 into the typechecking - the blanks will have all the info we need. This will probably be a lot simpler.check
and synthesize
).
I'd like TDNR to be more predictable. Lately I've been avoiding it since it gives odd results unless the rest of the code also typechecks (often not the case).
Something that might be relevant is that the way we currently select possibilities is by putting a blank in the program and then running typechecking to learn the type of that blank. If the program has type errors elsewhere, it's possible that inserting that blank may cause the program to typecheck, but with a weird type for the blank.
I'd probably start by collecting a number of examples of behavior we don't like for TDNR, then see if we can come up with something that's highly predictable.
Or it might be that TDNR is fine and just needs better docs and a few bugfixes.
Related issues:
377 #376 #370 #366 #845