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:
@ -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.".
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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".
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ a familiar, modern [syntax][].
|
||||
}
|
||||
}
|
||||
|
||||
var adjectives = new Fiber {
|
||||
var adjectives = Fiber.new {
|
||||
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user