1
0
forked from Mirror/wren

Make constructors just methods.

* Eliminate "new" reserved word.
* Allow "this" before a method definition to define a constructor.
* Only create a default constructor for classes that don't define one.
This commit is contained in:
Bob Nystrom
2015-07-10 09:18:22 -07:00
parent 0ddaa2517c
commit 5fb6186d7d
221 changed files with 864 additions and 654 deletions

View File

@ -64,7 +64,7 @@ class is fine:
And you can call each of the methods like so:
:::dart
var unicorn = new Unicorn
var unicorn = Unicorn.new()
unicorn.prance
unicorn.prance("Antwerp")
unicorn.prance("Brussels", "high noon")
@ -86,7 +86,7 @@ method that takes an *empty* argument list (`()`) and no argument list at all:
method() { "empty argument list" }
}
var confusing = new Confusing
var confusing = Confusing.new()
confusing.method // "no argument list".
confusing.method() // "empty argument list".
@ -159,39 +159,50 @@ overloading by arity, it's no problem for a class to define both.
## Constructors
To create a new instance of a class, you use the `new` keyword. We can make a
unicorn like so:
To create a new instance of a class, call a *constructor method* on its class.
By default, if you don't define any constructors yourself, you get a free one
named `new()`:
:::dart
new Unicorn
Unicorn.new()
You almost always want to define some state or do some other initialization on
a new object. For that, you'll want to define a constructor, like so:
However, you almost always want to define some state or do some other
initialization on a new object. For that, you'll want to define your own
constructor, like so:
:::dart
class Unicorn {
new {
IO.print("I am a constructor!")
}
}
When you create an instance with `new`, its constructor will be invoked. It's
just a method with a special name. Like methods, you can pass arguments to the
constructor by adding a parenthesized parameter list after `new`:
:::dart
class Unicorn {
new(name, color) {
this new(name, color) {
IO.print("My name is " + name + " and I am " + color + ".")
}
}
Values are passed to the constructor like so:
The `this` before the method name makes it a constructor. The `new` isn't
special. Constructors can have any name you like, which lets you clarify how it
creates the instance:
:::dart
new Unicorn("Flicker", "purple")
class Unicorn {
this brown(name) {
IO.print("My name is " + name + " and I am brown.")
}
}
Like other methods, you can overload constructors by [arity](#signature).
Constructors can obviously have arguments, and can be overloaded by
[arity](#signature). A constructor *must* be a named method with a (possibly
empty) argument list. Operators, getters, and setters cannot be constructors.
A constructor is actually a pair of methods. You get a method on the class:
:::dart
Unicorn.brown("Fred")
That creates the new instance, then it invokes the *initializer* on that
instance. This is where the constructor body you defined gets run.
This distinction is important because it means inside the body of the
constructor, you can access `this`, assign [fields](#fields), call superclass
constructors, etc.
## Fields
@ -292,8 +303,8 @@ And also instance methods. When you do so, there is still only one static field
shared among all instances of the class:
:::dart
var foo1 = new Foo
var foo2 = new Foo
var foo1 = Foo.new()
var foo2 = Foo.new()
foo1.setFromInstance("updated")
IO.print(foo2.baz) // updated.
@ -330,17 +341,36 @@ are not inherited.
Pegasus.canFly // ERROR: Static methods are not inherited.
Constructors, however, initialize the instance *after* it has been created.
They are defined as instance methods on the class and not on the metaclass.
That means that constructors *are* inherited.
This also means constructors are not inherited:
:::dart
class Unicorn {
new(name) {
this new(name) {
IO.print("My name is " + name + ".")
}
}
class Pegasus is Unicorn {}
new Pegasus("Fred") // Prints "My name is Fred.".
Pegasus.new("Fred") // Error!
Each class gets to control how it may be constructed independently of its base
classes. However, constructor *initializers* are inherited since those are
instance methods on the new object.
This means you can do `super` calls inside a constructor:
:::dart
class Unicorn {
this new(name) {
IO.print("My name is " + name + ".")
}
}
class Pegasus is Unicorn {
this new(name) {
super(name)
}
}
Pegasus.new("Fred") // Prints "My name is Fred.".

View File

@ -3,13 +3,13 @@
A lightweight coroutine. [Here](../fibers.html) is a gentle introduction.
### new **Fiber**(function)
### Fiber.**new**(function)
Creates a new fiber that executes `function` in a separate coroutine when the
fiber is run. Does not immediately start running the fiber.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print("I won't get printed")
}
@ -25,7 +25,7 @@ Pauses the current fiber and transfers control to the parent fiber. "Parent"
here means the last fiber that was started using `call` and not `run`.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print("Before yield")
Fiber.yield()
IO.print("After yield")
@ -41,7 +41,7 @@ If a yielded fiber is resumed by calling `call()` or `run()` with an argument,
`yield()` returns that value.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print(Fiber.yield()) // "value"
}
@ -65,7 +65,7 @@ Similar to `Fiber.yield` but provides a value to return to the parent fiber's
`call`.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
Fiber.yield("value")
}
@ -78,7 +78,7 @@ Similar to `Fiber.yield` but provides a value to return to the parent fiber's
Starts or resumes the fiber if it is in a paused state.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print("Fiber called")
Fiber.yield()
IO.print("Fiber called again")
@ -94,7 +94,7 @@ If the called fiber is resuming from a yield, the `yield()` method returns
`null` in the called fiber.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print(Fiber.yield())
}
@ -107,7 +107,7 @@ Invokes the fiber or resumes the fiber if it is in a paused state and sets
`value` as the returned value of the fiber's call to `yield`.
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print(Fiber.yield())
}

View File

@ -4,7 +4,7 @@
A first class function—an object that wraps an executable chunk of code.
[Here](../functions.html) is a friendly introduction.
### new **Fn**(function)
### Fn.**new**(function)
Creates a new function from... `function`. Of course, `function` is already a
function, so this really just returns the argument. It exists mainly to let you
@ -12,7 +12,7 @@ create a "bare" function when you don't want to immediately pass it as a [block
argument](../functions.html#block-arguments) to some other method.
:::dart
var fn = new Fn {
var fn = Fn.new {
IO.print("The body")
}
@ -25,15 +25,15 @@ It is a runtime error if `function` is not a function.
The number of arguments the function requires.
:::dart
IO.print(new Fn {}.arity) // 0.
IO.print(new Fn {|a, b, c| a }.arity) // 3.
IO.print(Fn.new {}.arity) // 0.
IO.print(Fn.new {|a, b, c| a }.arity) // 3.
### **call**(args...)
Invokes the function with the given arguments.
:::dart
var fn = new Fn { |arg|
var fn = Fn.new { |arg|
IO.print(arg)
}

View File

@ -109,11 +109,11 @@ To force eager evaluation, just call `.toList` on the result.
### **reduce**(function)
Reduces the sequence down to a single value. `function` is a function that takes
two arguments, the accumulator and sequence item and returns the new accumulator
value. The accumulator is initialized from the first item in the sequence. Then,
the function is invoked on each remaining item in the sequence, iteratively
updating the accumulator.
Reduces the sequence down to a single value. `function` is a function that
takes two arguments, the accumulator and sequence item and returns the new
accumulator value. The accumulator is initialized from the first item in the
sequence. Then, the function is invoked on each remaining item in the sequence,
iteratively updating the accumulator.
It is a runtime error to call this on an empty sequence.

View File

@ -52,7 +52,7 @@ superclasses) don't define that method, there's nothing Wren can do:
:::dart
class Foo {}
var foo = new Foo
var foo = Foo.new()
foo.someRandomMethod
If you run this, Wren will print:
@ -103,7 +103,7 @@ error message as a string.
For example, if you run this program:
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
123.badMethod
}

View File

@ -113,13 +113,13 @@ base class constructor:
:::dart
class Base {
new(arg) {
this new(arg) {
IO.print("base constructor got ", arg)
}
}
class Derived is Base {
new {
this new() {
super("value") // Prints "base constructor got value".
}
}

View File

@ -27,7 +27,7 @@ script, a main fiber is created for you automatically. You can spawn new fibers
using the `Fiber` class's constructor:
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print("This runs in a separate fiber.")
}
@ -48,7 +48,7 @@ until it passes control to another fiber. If it reaches the end of its body,
it's considered *done*:
:::dart
var fiber = new Fiber { IO.print("Hi") }
var fiber = Fiber.new { IO.print("Hi") }
fiber.isDone // false
fiber.call()
fiber.isDone // true
@ -70,7 +70,7 @@ fiber is called, it picks up right where it left off and keeps going.
You can make a fiber yield by calling the static `yield()` method on `Fiber`:
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
IO.print("fiber 1")
Fiber.yield()
IO.print("fiber 2")
@ -103,7 +103,7 @@ fiber has yielded and is waiting to resume, the value becomes the return value
of the `yield()` call:
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
var result = Fiber.yield()
IO.print(result)
}
@ -120,7 +120,7 @@ Fibers can also pass values *back* when they yield. If you pass an argument to
invoke the fiber:
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
Fiber.yield("sent")
}
@ -140,7 +140,7 @@ Wren's fibers can do that, but they can do much more. Like Lua, they are full
example:
:::dart
var fiber = new Fiber {
var fiber = Fiber.new {
(1..10).map {|i|
Fiber.yield(i)
}

View File

@ -61,7 +61,7 @@ but *don't* need to pass it to a method. For that, you can call the `Fn`
class's constructor:
:::dart
var someFn = new Fn {
var someFn = Fn.new {
IO.print("Hi!")
}
@ -91,7 +91,7 @@ method is dynamically-dispatched like any other, so you can define your own
}
}
blondie.callMe(new FakeFn)
blondie.callMe(FakeFn.new())
## Function parameters
@ -130,9 +130,9 @@ value using a `return` statement. In other words, these two functions do the
same thing:
:::dart
new Fn { "return value" }
Fn.new { "return value" }
new Fn {
Fn.new {
return "return value"
}
@ -146,7 +146,7 @@ leaving the scope where the function is defined:
class Counter {
static create {
var i = 0
return new Fn { i = i + 1 }
return Fn.new { i = i + 1 }
}
}

View File

@ -14,7 +14,7 @@ a familiar, modern [syntax][].
}
}
var adjectives = new Fiber {
var adjectives = Fiber.new {
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
}

View File

@ -56,12 +56,12 @@ Here's the same example in Wren:
:::dart
class Account {
new(balance) { _balance = balance }
this new(balance) { _balance = balance }
withdraw(amount) { _balance = _balance - amount }
}
// create and use an Account
var acc = new Account(1000)
var acc = Account.new(1000)
acc.withdraw(100)
Classes have a reputation for complexity because most of the widely used

View File

@ -28,8 +28,8 @@ Some people like to see all of the reserved words in a programming language in
one lump. If you're one of those folks, here you go:
:::dart
break class else false for foreign if import in is
new null return static super this true var while
break class else false for foreign if import in
is null return static super this true var while
## Identifiers
@ -102,12 +102,12 @@ compact notation:
:::dart
{ "single expression" }
If there is no newline after the `{` (or after the parameter list in a of
If there is no newline after the `{` (or after the parameter list in a
[function](functions.html)), then the block may only contain a single
expression, and it automatically returns the result of it. It's exactly the
same as doing:
:::dart
{
return "single expression"
return "single expression"
}