SO MUCH NEW DOCS OH WOW.
This commit is contained in:
+148
-49
@@ -2,9 +2,12 @@
|
||||
^category types
|
||||
|
||||
Every value in Wren is an object, and every object is an instance of a class.
|
||||
Even `true` and `false` are full-featured objects—instances of the `Bool` class.
|
||||
Even `true` and `false` are full-featured objects—instances of the `Bool`
|
||||
class.
|
||||
|
||||
Classes contain both *behavior* and *state*. Behavior is defined in *methods* which are stored in the class. State is defined in *fields*, whose values are stored in each instance.
|
||||
Classes contain both *behavior* and *state*. Behavior is defined in *methods*
|
||||
which are stored in the class. State is defined in *fields*, whose values are
|
||||
stored in each instance.
|
||||
|
||||
## Defining a class
|
||||
|
||||
@@ -26,7 +29,8 @@ To let our unicorn do stuff, we need to give it methods.
|
||||
}
|
||||
}
|
||||
|
||||
This defines a `prance` method that takes no arguments. To support parameters, add a parenthesized parameter list after the method's name:
|
||||
This defines a `prance` method that takes no arguments. To support parameters,
|
||||
add a parenthesized parameter list after the method's name:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
@@ -35,7 +39,12 @@ This defines a `prance` method that takes no arguments. To support parameters, a
|
||||
}
|
||||
}
|
||||
|
||||
Unlike most other dynamically-typed languages, in Wren you can have multiple methods in a class with the same name, as long as they take a different number of parameters. In technical terms, you can *overload by arity*. So this class is fine:
|
||||
### Arity
|
||||
|
||||
Unlike most other dynamically-typed languages, in Wren you can have multiple
|
||||
methods in a class with the same name, as long as they take a different number
|
||||
of parameters. In technical terms, you can *overload by arity*. So this class
|
||||
is fine:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
@@ -52,43 +61,29 @@ Unlike most other dynamically-typed languages, in Wren you can have multiple met
|
||||
}
|
||||
}
|
||||
|
||||
When you [call](method-calls.html) the `prance` method, it selects the right one based on how many arguments you pass it.
|
||||
|
||||
## Constructors
|
||||
|
||||
To create a new instance of a class, you use the `new` keyword. We can make a unicorn like so:
|
||||
And you can call each of the methods like so:
|
||||
|
||||
:::dart
|
||||
new Unicorn
|
||||
var unicorn = new Unicorn
|
||||
unicorn.prance
|
||||
unicorn.prance("Antwerp")
|
||||
unicorn.prance("Brussels", "high noon")
|
||||
|
||||
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:
|
||||
The number of arguments provided at the callsite determines which method is
|
||||
chosen.
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new {
|
||||
IO.print("I am a constructor!")
|
||||
}
|
||||
}
|
||||
It's often natural to have the same conceptual operation work with different
|
||||
sets of arguments. In other languages, you'd define a single method for the
|
||||
operation and have to check for "undefined" or missing arguments. Wren just
|
||||
treats them as different methods that you can implement separately.
|
||||
|
||||
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`:
|
||||
### Operators
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new(name, color) {
|
||||
IO.print("My name is " + name + " and I am " + color + ".")
|
||||
}
|
||||
}
|
||||
Operators are just special syntax for a method call on the left hand operand
|
||||
(or only operand in the case of unary operators like `!` and `~`). In other
|
||||
words, you can think of `a + b` as meaning `a.+(b)`.
|
||||
|
||||
Values are passed to the constructor like so:
|
||||
|
||||
:::dart
|
||||
new Unicorn("Flicker", "purple")
|
||||
|
||||
Like other methods, you can overload constructors by arity.
|
||||
|
||||
## Operators
|
||||
|
||||
Operators are just special syntax for regular [method calls](method-calls.html) on the left hand operand (or only operand in the case of unary operators like `!` and `~`). You can define them like so:
|
||||
You can define operators in your class like so:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
@@ -112,35 +107,143 @@ This can be used to define any of these operators:
|
||||
// Prefix:
|
||||
! ~ -
|
||||
|
||||
Note that `-` can be both a prefix and infix operator. If there's a parameter list, it's the infix one, otherwise, it's prefix. Since Wren supports overloading by arity, it's no problem for a class to define both.
|
||||
Note that `-` can be both a prefix and infix operator. If there's a parameter
|
||||
list, it's the infix one, otherwise, it's prefix. Since Wren supports
|
||||
overloading by arity, it's no problem for a class to define both.
|
||||
|
||||
Operator overloading is really useful for types like vectors and complex numbers where the reader knows what the operators will do, but can make code deeply confusing if overused. When in doubt, use a real name.
|
||||
### Subscript operators
|
||||
|
||||
## Setters
|
||||
**TODO**
|
||||
|
||||
### Setters
|
||||
|
||||
**TODO: ...**
|
||||
**TODO**
|
||||
|
||||
## Constructors
|
||||
|
||||
To create a new instance of a class, you use the `new` keyword. We can make a
|
||||
unicorn like so:
|
||||
|
||||
:::dart
|
||||
new Unicorn
|
||||
|
||||
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:
|
||||
|
||||
:::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) {
|
||||
IO.print("My name is " + name + " and I am " + color + ".")
|
||||
}
|
||||
}
|
||||
|
||||
Values are passed to the constructor like so:
|
||||
|
||||
:::dart
|
||||
new Unicorn("Flicker", "purple")
|
||||
|
||||
Like other methods, you can overload constructors by [arity](#arity).
|
||||
|
||||
## Fields
|
||||
|
||||
**TODO**
|
||||
All state stored in instances is stored in *fields*. Each field has a named
|
||||
that starts with an underscore.
|
||||
|
||||
:::dart
|
||||
class Rectangle {
|
||||
area { _width * _height }
|
||||
|
||||
// Other stuff...
|
||||
}
|
||||
|
||||
Here, `_width` and `_height` in the `area` [getter](classes.html#methods) refer
|
||||
to fields on the rectangle instance. You can think of them like `this.width`
|
||||
and `this.height` in other languages.
|
||||
|
||||
When a field name appears, Wren looks for the nearest enclosing class and looks
|
||||
up the field on the instance of that class. Field names cannot be used outside
|
||||
of an instance method. They *can* be used inside a [function](functions.html)
|
||||
in a method. Wren will look outside any nested functions until it finds an
|
||||
enclosing method.
|
||||
|
||||
Unlike [variables](variables.html), fields are implicitly declared by simply
|
||||
assigning to them. If you access a field before it has been initialized, its
|
||||
value is `null`.
|
||||
|
||||
### Encapsulation
|
||||
|
||||
All fields are *protected* in Wren—an object's fields can only be
|
||||
directly accessed from within methods defined on the object's class, or its
|
||||
superclasses. You cannot even access fields on another instance of your own
|
||||
class, unlike C++ and Java.
|
||||
|
||||
If you want to make a property of an object visible, you need to define a
|
||||
getter to expose it:
|
||||
|
||||
:::dart
|
||||
class Rectangle {
|
||||
width { _width }
|
||||
height { _height }
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
To allow outside code to modify the field, you'll also need to provide setters:
|
||||
|
||||
:::dart
|
||||
class Rectangle {
|
||||
width=(value) { _width = value }
|
||||
height=(value) { _height = value }
|
||||
}
|
||||
|
||||
One thing we've learned in the past forty years of software engineering is that
|
||||
encapsulating state tends to make code easier to maintain, so Wren defaults to
|
||||
keeping your object's state pretty tightly bundled up. Don't feel that you have
|
||||
to or even should define getters or setters for most of your object's fields.
|
||||
|
||||
## Metaclasses and static members
|
||||
|
||||
**TODO**
|
||||
|
||||
### Static fields
|
||||
|
||||
A name that starts with *two* underscores is a *static* field. They work
|
||||
similar to [fields](#fields) except the data is stored on the class itself, and
|
||||
not the instance. They can be used in *both* instance and static methods.
|
||||
|
||||
**TODO: Example.**
|
||||
|
||||
|
||||
## Inheritance
|
||||
|
||||
A class can inherit from a "parent" or *superclass*. When you invoke a method on an object of some class, if it can't be found, it walks up the chain of superclasses looking for it there.
|
||||
A class can inherit from a "parent" or *superclass*. When you invoke a method
|
||||
on an object of some class, if it can't be found, it walks up the chain of
|
||||
superclasses looking for it there.
|
||||
|
||||
By default, any new class inherits from `Object`, which is the superclass from which all other classes ultimately descend. You can specify a different parent class using `is` when you declare the class:
|
||||
By default, any new class inherits from `Object`, which is the superclass from
|
||||
which all other classes ultimately descend. You can specify a different parent
|
||||
class using `is` when you declare the class:
|
||||
|
||||
:::dart
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
This declares a new class `Pegasus` that inherits from `Unicorn`.
|
||||
|
||||
The metaclass hierarchy does *not* parallel the regular class hierarchy. So, if `Pegasus` inherits from `Unicorn`, `Pegasus`'s metaclass will not inherit from `Unicorn`'s metaclass. In more prosaic terms, this means that static methods are not inherited.
|
||||
The metaclass hierarchy does *not* parallel the regular class hierarchy. So, if
|
||||
`Pegasus` inherits from `Unicorn`, `Pegasus`'s metaclass will not inherit from
|
||||
`Unicorn`'s metaclass. In more prosaic terms, this means that static methods
|
||||
are not inherited.
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
@@ -152,7 +255,9 @@ The metaclass hierarchy does *not* parallel the regular class hierarchy. So, if
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
@@ -164,9 +269,3 @@ Constructors, however, initialize the instance *after* it has been created. They
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
new Pegasus("Fred") // Prints "My name is Fred.".
|
||||
|
||||
## Superclass method calls
|
||||
|
||||
**TODO**
|
||||
|
||||
**TODO: this**
|
||||
|
||||
Reference in New Issue
Block a user