koka-lang / koka

Koka language compiler and interpreter

Home Page:http://koka-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Strange compiler error on ambiguous names

chtenb opened this issue · comments

The problem
Minimal repro:

ref type type1
  Field(type2 : type2)

ref struct type2
  flag : bool

fun flag(eval : type1)
  match eval
    Field(type2) -> type2.flag

Error:

repro.kk(9,21): error: types do not match
 context      :                     type2.flag
 term         :                     type2
 inferred type: type2
 expected type: type1

Expected behavior
I had expected a different error, something along the lines of error: flag is already defined at (5, 2), if the names are ambiguous. If they are not ambiguous I would have expected no error at all.

This is interesting because it desugars to the following:

ref type type1
  Field(type2 : type2)

ref struct type2
  flag : bool

fun type2/flag(t: type2)
   match t
     Type2(f) -> f

fun flag(eval : type1)
  match eval
    Field(type2) -> type2.flag

which since flag is unqualified and there is there is an unqualified name exactly matching, it complains of a type error, instead of attempting to disambiguate.

If you change your flag for type1 to:

fun type1/flag(eval : type1)
  match eval
    Field(type2) -> type2.flag

Then it resolves fine. So it is an issue that mixing qualified names with unqualified names within a module doesn't work well. The error needs to be changed to reflect that, or we need to fall back to disambiguation when encountering a type error in this case. Probably the latter.

commented

Hi @chtenb -- thanks for such early feedback :-) Thanks Tim for the desugaring -- but it is a bit more subtle than that (and I think we should consider the current behavior a bug).

In particular, in the "local scope" any exact match is always taken -- so a local val x = .. will always be the one considered for an identifier x. This is to preserve the regular scoping rules of lambda calculus. For implicit parameters we get around this "restriction" by using a locally qualified name for them (i.e. ?eq == @implicit/eq and thus a bare identifier eq is not a direct match and can consider things in the global scope as well.

For your example, the flag identifier is not a direct match with the global flag as it's qualified name is currentmodule/flag -- so why did the compiler not consider type2/flag as well? Well, that is because the definition is recursive and it has not a full type signature. In such case, the definition cannot be used for polymorphic recursion, and to prevent this the compiler inserts a monomorphic signature in the local scope ... and now the flag matches exactly while type checking :-( . So if you complete the type of type1/flag, it will work:

fun flag(eval : type1) : bool
  match eval
    Field(type2) -> type2.flag

(or if you locally qualify as Tim suggested).

The current behavior is a bug.. but a bit tricky to fix I think as we would need a qualified name in the local scope; I'll look into it.

commented

I think I fixed this issue in the latest dev -- thanks!