2013-11-21 21:38:36 -08:00
^title Classes
2014-04-14 21:23:46 -07:00
^category types
2013-11-21 21:38:36 -08:00
2013-11-23 14:52:50 -08:00
Every value in Wren is an object, and every object is an instance of a class.
2015-09-01 08:16:04 -07:00
Even `true` and `false` are full-featured objects— instances of the
[`Bool` ](core/bool.html ) class.
2013-11-23 14:52:50 -08:00
2015-01-03 23:27:02 -08:00
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.
2013-12-28 09:37:41 -08:00
2013-11-23 14:52:50 -08:00
## Defining a class
Classes are created using the `class` keyword, unsurprisingly:
2015-09-22 07:59:54 -07:00
:::wren
2013-11-23 14:52:50 -08:00
class Unicorn {}
This creates a class named `Unicorn` with no methods or fields.
2014-02-04 08:45:08 -08:00
## Methods
2013-12-28 09:37:41 -08:00
2014-10-15 06:36:42 -07:00
To let our unicorn do stuff, we need to give it methods.
2013-12-28 09:37:41 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2013-12-28 09:37:41 -08:00
class Unicorn {
2015-09-01 08:16:04 -07:00
prance() {
2015-09-15 07:46:09 -07:00
System.print("The unicorn prances in a fancy manner!")
2013-12-28 09:37:41 -08:00
}
}
2015-09-01 08:16:04 -07:00
This defines a `prance()` method that takes no arguments. To support
parameters, put their names inside the parentheses:
2013-12-28 09:37:41 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2014-01-30 06:51:52 -08:00
class Unicorn {
prance(where, when) {
2015-09-15 07:46:09 -07:00
System.print("The unicorn prances in " + where + " at " + when)
2014-01-30 06:51:52 -08:00
}
2013-12-28 09:37:41 -08:00
}
2015-02-26 23:08:36 -08:00
### Signature
2015-01-03 23:27:02 -08:00
Unlike most other dynamically-typed languages, in Wren you can have multiple
2015-02-26 23:08:36 -08:00
methods in a class with the same name, as long as they have a different
2015-09-01 08:16:04 -07:00
*signature*. In technical terms, you can *overload by arity* . So this class is
fine:
2013-12-28 09:37:41 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2013-12-28 09:37:41 -08:00
class Unicorn {
2015-09-01 08:16:04 -07:00
prance() {
2015-09-15 07:46:09 -07:00
System.print("The unicorn prances in a fancy manner!")
2013-12-28 09:37:41 -08:00
}
prance(where) {
2015-09-15 07:46:09 -07:00
System.print("The unicorn prances in " + where)
2013-12-28 09:37:41 -08:00
}
prance(where, when) {
2015-09-15 07:46:09 -07:00
System.print("The unicorn prances in " + where + " at " + when)
2013-12-28 09:37:41 -08:00
}
}
2015-01-03 23:27:02 -08:00
And you can call each of the methods like so:
2015-09-22 07:59:54 -07:00
:::wren
2015-07-10 09:18:22 -07:00
var unicorn = Unicorn.new()
2015-09-01 08:16:04 -07:00
unicorn.prance()
2015-01-03 23:27:02 -08:00
unicorn.prance("Antwerp")
unicorn.prance("Brussels", "high noon")
The number of arguments provided at the callsite determines which method is
chosen.
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.
2015-09-01 08:16:04 -07:00
### Getters
Many methods on a class exist to expose or compute some property of the object.
For example:
2015-09-22 07:59:54 -07:00
:::wren
2015-10-18 15:56:52 -07:00
System.print("string".count) //> 6
2015-09-01 08:16:04 -07:00
These *getters* are just another kind of method— one without a parameter
list. You can define them like so:
2015-09-22 07:59:54 -07:00
:::wren
2015-09-01 08:16:04 -07:00
class Unicorn {
isFancy { true } // Unicorns are always fancy.
}
Whether or not a method name has parentheses is also part of its signature.
This lets you distinguish between a method that takes an *empty* argument list
(`()` ) and no argument list at all:
2015-02-26 23:08:36 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2015-02-26 23:08:36 -08:00
class Confusing {
method { "no argument list" }
method() { "empty argument list" }
}
2015-07-10 09:18:22 -07:00
var confusing = Confusing.new()
2015-02-26 23:08:36 -08:00
confusing.method // "no argument list".
confusing.method() // "empty argument list".
Like the example says, having two methods that differ just by an empty set of
2015-09-01 08:16:04 -07:00
parentheses is pretty confusing. That's not what this is for. Instead, it
ensures that the way you *declare* the method is the way you *call* it.
Unlike other languages with "optional parentheses", Wren wants to make sure you
call a getter like a getter and a `()` method like a `()` method. These don't
work:
2015-09-22 07:59:54 -07:00
:::wren
2015-09-01 08:16:04 -07:00
"string".count()
list.clear
2015-02-26 23:08:36 -08:00
2015-09-01 08:16:04 -07:00
Methods that don't need arguments and don't modify the underlying object are
usually getters:
2015-02-26 23:08:36 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2015-02-26 23:08:36 -08:00
"string".count
2015-09-01 08:16:04 -07:00
(1..10).min
1.23.sin
[1, 2, 3].isEmpty
2015-02-26 23:08:36 -08:00
2015-09-01 08:16:04 -07:00
When a method doesn't need any parameters, but does modify the object, it's
helpful to draw attention to that by requiring an empty set of parentheses:
2015-02-26 23:08:36 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2015-02-26 23:08:36 -08:00
list.clear()
2015-09-01 08:16:04 -07:00
Also, when a method supports multiple arities, it's typical to include the `()`
in the zero-argument case to be consistent with the other versions:
2015-02-26 23:08:36 -08:00
2015-09-01 08:16:04 -07:00
Fn.new { "a function" }.call()
Fiber.yield()
2015-02-26 23:08:36 -08:00
2015-01-03 23:27:02 -08:00
### Operators
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)` .
You can define operators in your class like so:
2015-09-22 07:59:54 -07:00
:::wren
2015-01-03 23:27:02 -08:00
class Unicorn {
// Infix:
+(other) {
2015-09-15 07:46:09 -07:00
System.print("Adding to a unicorn?")
2015-01-03 23:27:02 -08:00
}
// Prefix:
! {
2015-09-15 07:46:09 -07:00
System.print("Negating a unicorn?!")
2015-01-03 23:27:02 -08:00
}
}
This can be used to define any of these operators:
2015-09-22 07:59:54 -07:00
:::wren
2015-01-03 23:27:02 -08:00
// Infix:
+ - * / % < > < = >= == != & |
// 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.
### Subscript operators
**TODO**
### Setters
**TODO**
2013-12-28 09:37:41 -08:00
## Constructors
2015-09-01 08:16:04 -07:00
Unicorns can prance around now, 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:
2014-02-04 08:45:08 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2014-02-04 08:45:08 -08:00
class Unicorn {
2015-07-21 07:24:53 -07:00
construct new(name, color) {
2015-09-15 07:46:09 -07:00
System.print("My name is " + name + " and I am " + color + ".")
2014-02-04 08:45:08 -08:00
}
}
2015-09-01 08:16:04 -07:00
The `construct` keyword says we're defining a constructor, and `new` is its
name. In Wren, all constructors have names, just like [methods][#methods ]. The
word "new" isn't special to Wren, it's just a common constructor name.
To make a unicorn now, we just call the constructor method on the class itself:
2015-09-22 07:59:54 -07:00
:::wren
2015-09-01 08:16:04 -07:00
var fred = Unicorn.new("Fred", "palomino")
Giving constructors names is handy because it means you can have more than one,
and each can clarify how it creates the instance:
2014-02-04 08:45:08 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2014-02-04 08:45:08 -08:00
class Unicorn {
2015-07-21 07:24:53 -07:00
construct brown(name) {
2015-09-15 07:46:09 -07:00
System.print("My name is " + name + " and I am brown.")
2014-02-04 08:45:08 -08:00
}
}
2015-09-01 08:16:04 -07:00
var dave = Unicorn.brown("Dave")
Note that we have to declare a constructor because, unlike some other
languages, Wren doesn't give you a default one. This is useful because some
classes aren't designed to be constructed. If you have an abstract base class
that just contains methods to be inherited by other classes, it doesn't need
and won't have a constructor.
Like other methods, 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.
2015-07-10 09:18:22 -07:00
A constructor is actually a pair of methods. You get a method on the class:
2014-02-04 08:45:08 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2015-09-01 08:16:04 -07:00
Unicorn.brown("Dave")
2015-07-10 09:18:22 -07:00
That creates the new instance, then it invokes the *initializer* on that
instance. This is where the constructor body you defined gets run.
2013-12-28 09:37:41 -08:00
2015-07-10 09:18:22 -07:00
This distinction is important because it means inside the body of the
constructor, you can access `this` , assign [fields ](#fields ), call superclass
constructors, etc.
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
## Fields
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
All state stored in instances is stored in *fields* . Each field has a named
that starts with an underscore.
2014-04-08 21:18:17 -07:00
2015-09-22 07:59:54 -07:00
:::wren
2015-01-03 23:27:02 -08:00
class Rectangle {
area { _width * _height }
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
// Other stuff...
2014-04-08 21:18:17 -07:00
}
2015-01-03 23:27:02 -08:00
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.
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
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.
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
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` .
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
### Encapsulation
2014-04-08 21:18:17 -07:00
2015-03-27 06:46:12 -07:00
All fields are *private* in Wren— an object's fields can only be directly
accessed from within methods defined on the object's class. You cannot even
access fields on another instance of your own class, unlike C++ and Java.
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
If you want to make a property of an object visible, you need to define a
getter to expose it:
2014-04-08 21:18:17 -07:00
2015-09-22 07:59:54 -07:00
:::wren
2015-01-03 23:27:02 -08:00
class Rectangle {
width { _width }
height { _height }
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
// ...
}
2014-04-08 21:18:17 -07:00
2015-01-03 23:27:02 -08:00
To allow outside code to modify the field, you'll also need to provide setters:
2013-12-28 09:37:41 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2015-01-03 23:27:02 -08:00
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.
2013-12-28 09:37:41 -08:00
## Metaclasses and static members
**TODO**
2013-11-23 14:52:50 -08:00
2015-01-03 23:27:02 -08:00
### 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.
2015-09-22 07:59:54 -07:00
:::wren
2015-02-28 11:26:18 -05:00
class Foo {
2015-03-01 08:39:53 -08:00
// Set the static field.
static set(a) {
__a = a
}
setFromInstance(a) {
__a = a
}
// Can use __a in both static methods...
static bar { __a }
// ...and instance ones.
baz { __a }
2015-02-28 11:26:18 -05:00
}
2015-01-03 23:27:02 -08:00
2015-03-01 08:39:53 -08:00
Just like instance fields, static fields are initially `null` :
2015-09-22 07:59:54 -07:00
:::wren
2015-10-18 15:56:52 -07:00
System.print(Foo.bar) //> null
2015-03-01 08:39:53 -08:00
They can be used from static methods:
2015-09-22 07:59:54 -07:00
:::wren
2015-02-28 11:26:18 -05:00
Foo.set("foo")
2015-10-18 15:56:52 -07:00
System.print(Foo.bar) //> foo
2015-03-01 08:39:53 -08:00
And also instance methods. When you do so, there is still only one static field
shared among all instances of the class:
2015-09-22 07:59:54 -07:00
:::wren
2015-07-10 09:18:22 -07:00
var foo1 = Foo.new()
var foo2 = Foo.new()
2015-03-01 08:39:53 -08:00
foo1.setFromInstance("updated")
2015-10-18 15:56:52 -07:00
System.print(foo2.baz) //> updated
2015-01-03 23:27:02 -08:00
2013-11-23 14:52:50 -08:00
## Inheritance
2015-01-03 23:27:02 -08:00
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.
2013-11-23 14:52:50 -08:00
2015-01-03 23:27:02 -08:00
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:
2013-11-23 14:52:50 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2013-11-23 14:52:50 -08:00
class Pegasus is Unicorn {}
This declares a new class `Pegasus` that inherits from `Unicorn` .
2015-01-07 16:41:30 +00:00
Note that you should not create classes that inherit from the built-in types (Bool, Num, String, Range, List). The built-in types expect their internal bit representation to be very specific and get horribly confused when you invoke one of the inherited built-in methods on the derived type.
2015-01-06 08:37:57 +00:00
2015-01-03 23:27:02 -08:00
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.
2013-12-28 09:37:41 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2014-10-15 06:36:42 -07:00
class Unicorn {
// Unicorns cannot fly. :(
static canFly { false }
}
class Pegasus is Unicorn {}
2015-10-18 15:56:52 -07:00
Pegasus.canFly //! Static methods are not inherited.
2014-10-15 06:36:42 -07:00
2015-07-10 09:18:22 -07:00
This also means constructors are not inherited:
2013-12-28 09:37:41 -08:00
2015-09-22 07:59:54 -07:00
:::wren
2014-10-15 06:36:42 -07:00
class Unicorn {
2015-10-18 15:56:52 -07:00
construct new(name) {
2015-09-15 07:46:09 -07:00
System.print("My name is " + name + ".")
2014-10-15 06:36:42 -07:00
}
}
class Pegasus is Unicorn {}
2015-10-18 15:56:52 -07:00
Pegasus.new("Fred") //! Pegasus does not define new().
2015-07-10 09:18:22 -07:00
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:
2015-09-22 07:59:54 -07:00
:::wren
2015-07-10 09:18:22 -07:00
class Unicorn {
2015-10-18 15:56:52 -07:00
construct new(name) {
2015-09-15 07:46:09 -07:00
System.print("My name is " + name + ".")
2015-07-10 09:18:22 -07:00
}
}
class Pegasus is Unicorn {
2015-10-18 15:56:52 -07:00
construct new(name) {
2015-07-10 09:18:22 -07:00
super(name)
}
}
2015-10-18 15:56:52 -07:00
Pegasus.new("Fred") //> My name is Fred