From fb72e0fff786456406bfd0bee49a343c26d20161 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sat, 21 Nov 2015 14:00:21 -0800 Subject: [PATCH] Document method scope. --- doc/site/classes.markdown | 168 +++++++++++++++++++++++++++++------- doc/site/style.scss | 7 ++ doc/site/variables.markdown | 2 - 3 files changed, 146 insertions(+), 31 deletions(-) diff --git a/doc/site/classes.markdown b/doc/site/classes.markdown index 26ece8e5..5f33a7c1 100644 --- a/doc/site/classes.markdown +++ b/doc/site/classes.markdown @@ -91,6 +91,9 @@ A setter has `=` after the name, followed by a single parenthesized parameter: } } +By convention, the parameter is usually named `value` but you can call it +whatever makes your heart flutter. + ### Operators Prefix operators, like getters, have no parameter list: @@ -139,29 +142,138 @@ operator and a setter: } } -## This +## Method Scope -**TODO: Integrate this into the page better.** +Up to this point, "[scope][]" has been used to talk exclusively about +[variables][]. In a procedural language like C, or a functional one like Scheme, +that's the only kind of scope there is. But object-oriented languages like Wren +introduce another kind of scope: *object scope*. It contains the methods that +are available on an object. When you write: -The special `this` keyword works sort of like a variable, but has special -behavior. It always refers to the instance whose method is currently being -executed. This lets you invoke methods on "yourself". +[scope]: variables.html#scope +[variables]: variables.html + + :::wren + unicorn.isFancy + +You're saying "look up the method `isFancy` in the scope of the object +`unicorn`". In this case, the fact that you want to look up a *method* `isFancy` +and not a *variable* `isFancy` is explicit. That's what `.` does and the +object to the left of the period is the object you want to look up the method on. + +### `this` + +Things get more interesting when you're inside the body of a method. When the +method is called on some object and the body is being executed, you often need +to access that object itself. You can do that using `this`. + + :::wren + class Unicorn { + name { "Francis" } + + printName() { + System.print(this.name) //> Francis + } + } + +The `this` keyword works sort of like a variable, but has special behavior. It +always refers to the instance whose method is currently being executed. This +lets you invoke methods on "yourself". It's an error to refer to `this` outside of a method. However, it's perfectly -fine to use it inside a function contained in a method. When you do, `this` -still refers to the instance whose method is being called. +fine to use it inside a [function][] contained in a method. When you do, `this` +still refers to the instance whose *method* is being called. + +[function]: functions.html This is unlike Lua and JavaScript which can "forget" `this` when you create a callback inside a method. Wren does what you want here and retains the -reference to the original object. (In technical terms, a function's closure -includes `this`.) +reference to the original object. + +(In technical terms, a function's closure includes `this`. Wren can do this +because it makes a distinction between methods and functions.) + +### Implicit `this` + +Using `this.` every time you want to call a method on yourself works, but it's +tedious and verbose, which is why some languages don't require it. You can do a +"self send" by calling a method (or getter or setter) without any explicit +receiver: + + :::wren + class Unicorn { + name { "Francis" } + + printName() { + System.print(name) //> Francis + } + } + +Code like this gets tricky when you there is also a variable outside of the +class with the same name. Consider: + + :::wren + var name = "variable" + + class Unicorn { + name { "Francis" } + + printName() { + System.print(name) // ??? + } + } + +Should `printName()` print "variable" or "Francis"? A method body has a foot in +each of two worlds. It is surrounded by the lexical scope where it's defined in +the program, but it also has the object scope of the methods on `this`. + +Which scope wins? Every language has to decide how to handle this and there +is a surprising plethora of approaches. Wren's approach to resolving a name +inside a method works like this: + +1. If there is a local variable inside the method with that name, that wins. +2. Else, if the name starts with a lowercase letter, treat it like a method on + `this`. +3. Otherwise, look for a variable with that name in the surrounding scope. + +So, in the above example, we hit case #2 and it prints "Francis". Distinguishing +self sends from outer variables based on the *case* of the first letter in the +name probably seems crazy but it works surprisingly well. Method names are +lowercase in Wren. Class names are capitalized. + +Most of the time, when you're in a method and want to access a name from outside +of the class, it's usually the name of some other class. This rule makes that +work. + +Here's an example that shows all three cases: + + :::wren + var shadowed = "surrounding" + var lowercase = "surrounding" + var Capitalized = "surrounding" + + class Scope { + shadowed { "object" } + lowercase { "object" } + Capitalized { "object" } + + test() { + var shadowed = "local" + + System.print(shadowed) //> local + System.print(lowercase) //> object + System.print(Capitalized) //> surrounding + } + } + +It's a bit of a strange rule, but Ruby works more or less the same way. ## Constructors -Unicorns can prance around now (as well as a bunch of weird operators that don't -make sense outside of these examples), but we don't actually *have* any unicorns -to do it. To create instances of a class, we need a *constructor*. You can -define one like so: +We've seen how to define kinds of objects and how to declare methods on them. +Our unicorns can prance around, but we don't actually *have* any unicorns to do +it. To create *instances* of a class, we need a *constructor*. You define one +like so: :::wren class Unicorn { @@ -282,32 +394,30 @@ not the instance. They can be used in *both* instance and static methods. :::wren class Foo { - // Set the static field. - static set(a) { - __a = a + construct new() {} + + static setFromStatic(a) { __a = a } + setFromInstance(a) { __a = a } + + static printFromStatic() { + System.print(__a) } - setFromInstance(a) { - __a = a + printFromInstance() { + System.print(__a) } - - // Can use __a in both static methods... - static bar { __a } - - // ...and instance ones. - baz { __a } } Just like instance fields, static fields are initially `null`: :::wren - System.print(Foo.bar) //> null + Foo.printFromStatic() //> null They can be used from static methods: :::wren - Foo.set("foo") - System.print(Foo.bar) //> foo + Foo.setFromStatic("first") + Foo.bar.printFromStatic() //> first And also instance methods. When you do so, there is still only one static field shared among all instances of the class: @@ -316,8 +426,8 @@ shared among all instances of the class: var foo1 = Foo.new() var foo2 = Foo.new() - foo1.setFromInstance("updated") - System.print(foo2.baz) //> updated + foo1.setFromInstance("second") + foo2.printFromInstance() //> second ## Inheritance diff --git a/doc/site/style.scss b/doc/site/style.scss index 9dc5e314..c33cc6b6 100644 --- a/doc/site/style.scss +++ b/doc/site/style.scss @@ -176,6 +176,13 @@ h3 { font: 20px $body; margin: 24px 0 0 0; color: $link; + + code { + border: none; + background: inherit; + color: inherit; + font-size: 20px; + } } a { diff --git a/doc/site/variables.markdown b/doc/site/variables.markdown index a23dc351..7af2200f 100644 --- a/doc/site/variables.markdown +++ b/doc/site/variables.markdown @@ -71,7 +71,5 @@ then it isn't an assignment. Instead, it's calling a [setter method][]. [setter method]: method-calls.html#setters -**TODO: Top-level names.** - Functions → ← Control Flow