diff --git a/doc/receiver-less calls 2.txt b/doc/receiver-less calls 2.txt new file mode 100644 index 00000000..4a5bbe7e --- /dev/null +++ b/doc/receiver-less calls 2.txt @@ -0,0 +1,146 @@ + var baz = "top level" + + class Foo { + bar { + baz + _baz + } + + baz { "getter" } + _baz { "private getter" } + + this { + _baz = "field" + } + } + +Given `_foo`, how do we tell if it is: +1. A call to a private getter +2. Accessing a private field +3. Tearing off a reference to a private method + +It's not 3 because of arity overloading. Wren doesn't really have method +tear-off because of this.) + +This is hard because the getter may not be defined yet. One option is: +It's always a call to a private getter. After the class is defined, we see if +there are any private getters that were not implemented and define implicit +getters for them that return fields. + +That's weird if you take into account setters, though. Consider: + + class Foo { + a { io.write(_prop) } + _prop = value { ... } + } + +For first reference to `_prop`, compile it to getter call. Then see setter +defined for it, so we no longer implicitly make a field. But there's no getter, +so now the above call will fail. + +Probably do want call to fail here, so that may be OK. + +--- + +Given `_foo(arg)`, how do we tell if it is: + +1. A call to a private method +2. A call to a private getter, which returns a field that's a fn, and invoking + it. + +Since arity is part of the name, the answer here is 1. + +--- + +Given `foo(arg)` inside a class, how do we tell if it is: + +1. A call to a method on this. +2. Accessing a field `foo` on this, which returns a fn, and invoking it. +3. Calling a getter `foo` on this, which returns a fn, and invoking it. +4. A call to a top-level fn. + +Let's just dismiss 3. Since arity affects naming, `foo(arg)` and `(foo)(arg)` +are really different things in Wren. The parentheses and args are effectively +part of the name. + +That covers 2 as well. If we ditch top level fns, we're left with 1. This is +good, I think. It means the common case of calling methods on yourself is nice +and terse. + +--- + +Given `foo` inside a class, how do we tell if it is: + +1. Accessing a field on this. +2. Calling a getter on this. +3. Accessing a global variable. +4. Accessing a top-level getter. +5. Accessing a local variable. + +We can probably ditch 4. We can ditch 1 because Wren doesn't have public fields. + +Because both getters and global variables can be used before they are defined, +we can't determine statically (in a single pass compiler) if there is a global +variable or getter named `foo` in order to disambiguate. Even if we could, we'd +still have to answer the ambiguous case where it's both. + +If we assume it's a global and the user wants a getter, they can always do +`this.foo` to be explicit. If we assume it's getter, how would they indicate a +global? + +One option is to have a different naming convention for globals, like a +capitalized initial variable. That lines up with class names at the top level +anyway. It just means if we have variables for imported modules, we'll want to +capitalize those. + +We still have to distinguish locals, but since those are declared before use, we +can determine that statically. I.e. locals will shadow implicit getters. + +--- + +OK, so here's one proposal: + + class MyClass { + method { + _foo // access field + _foo(arg) // not valid + foo // local var or getter on this + foo(arg) // method on this + Foo // global variable + } + } + +This is simple, and straightforward to compile. Using capitalization for globals +is a bit weird, and not having private methods is a bummer, but maybe simplicity +is the right answer. + +Here's another: + + class MyClass { + method { + _foo // private getter on this + _foo(arg) // private method on this + foo // local var or getter on this + foo(arg) // method on this + Foo // global variable + } + } + +To get rid of the weird capitalization rule for globals, one option is to not +allow forward references to globals. That would break mutually recursive +references to classes, though: + + class A { + foo { B.new } + } + + class B { + foo { A.new } + } + +So, not a fan of that. + +Ignoring that, the main difference between the two proposals is the second has +private methods. Since the first proposal is practically a subset of the second, +let's start with that one first. +