Operators¶
The operators of the language resolve through the core traits, which
are in scope in every program. + resolves Add for its operand type,
== resolves Eq, < resolves Ord, xs[i] resolves Index and
xs[a:b] resolves Slice. The built-in types implement the traits they
have natively; a type of the program implements one to take the operator
for its own values, and changes nothing else.
| Operator | Trait | Function |
|---|---|---|
a + b |
Add |
add(self, other: Self) : Self |
a - b |
Sub |
sub(self, other: Self) : Self |
a * b |
Mul |
mul(self, other: Self) : Self |
a / b |
Div |
div(self, other: Self) : Self |
a % b |
Rem |
rem(self, other: Self) : Self |
a == b, a != b |
Eq |
eq(self, other: Self) : Bool |
a < b and the other comparisons, sort, min, max |
Ord |
compare(self, other: Self) : Ordering |
xs[i] |
Index<T> |
at(self, position: Int) : Option<T> |
xs[a:b] |
Slice |
slice(self, range: Range) : Self |
and, or and not are language-defined and resolve through nothing, and
so are |, = and :.
Arithmetic¶
An arithmetic operator takes two values of one type. For Int, Float and
Decimal that is built in, except that an Int beside a Decimal widens
to one; + over two strings is concatenation, since String implements
Add. Any other type resolves the operator through its trait:
type Money { cents : Int }
impl Sub for Money {
fn sub(self, other: Money) : Money = Money(cents = self.cents - other.cents)
}
impl Mul for Money {
fn mul(self, other: Money) : Money = Money(cents = self.cents * other.cents)
}
[(Money(cents = 500) - Money(cents = 120)).cents, (Money(cents = 3) * Money(cents = 4)).cents]
[380, 12]
A type without the implementation does not take the operator, and the message names the trait:
type Vec2 { x : Int, y : Int }
Vec2(x = 1, y = 2) + Vec2(x = 3, y = 4)
check error[type-mismatch]: addition requires two Int, two Float or two Decimal operands, or two values of a type implementing `Add`, not Vec2
--> <expression>:2:1
|
2 | Vec2(x = 1, y = 2) + Vec2(x = 3, y = 4)
| ^^^^^^^^^^^^^^^^^^
An operator is a function value where a function is expected, so
reduce(0, +) sums a list and reduce("", +) joins strings:
[[1, 2, 3] | reduce(0, +), ["a", "b"] | reduce("", +)]
[6, "ab"]
Equality¶
== over a type implementing Eq calls its eq. Without one, equality is
the type's own rule: a record field by field, a list position by position,
a set when each holds an equal of every element of the other, a map key by
key. A type that wants another equality declares it:
type Email { address : String }
impl Eq for Email {
fn eq(self, other: Email) : Bool = self.address.lower() == other.address.lower()
}
[Email(address = "Ada@Example.org") == Email(address = "ada@example.org"), Email(address = "a") != Email(address = "b")]
[true, true]
Order¶
A type implementing Ord supplies compare, which gives an Ordering,
Less, Equal or Greater, and with it the type is Orderable: < and
the other comparisons, sort, min and max take it, and it may be a
SortedMap key.
type Version { major : Int, minor : Int }
impl Ord for Version {
fn compare(self, other: Version) : Ordering =
compare(self.major * 100 + self.minor, other.major * 100 + other.minor)
}
versions = [Version(major = 1, minor = 10), Version(major = 1, minor = 2), Version(major = 0, minor = 9)]
[versions | sort | map(v => fmt("{}.{}", v.major, v.minor)), [Version(major = 1, minor = 2) < Version(major = 1, minor = 10)] | map(str)]
[["0.9", "1.2", "1.10"], ["true"]]
A record without Ord has no order of its own. sort over it is a type
error, and the message says to sort with an explicit key, sort(by = v => v.minor),
which needs only the key's order.
Positions¶
xs[i] over a type implementing Index<T> is xs.at(i): the element at
the position, or none past the end. The standard library implements it
for sequences, so a List and a String take it, and a program's type
takes it with one implementation, as generics did for a
Ring<T>.
[["a", "b", "c"][1], ["a", "b", "c"][5]]
["b", none]
A Set has no positions and a Map has keys, and neither implements
Index, so the bracket over them is a type error.
Parts¶
xs[a:b] over a type implementing Slice is a part of the value, of the
value's own type. A bound left out is open, and a negative bound counts
from the end:
["hello"[1:3], "hello"[-2:]]
["el", "lo"]
The bracket holds either an Int, a position, or a Range, a slice, and
the type of what is inside decides which. Range(start = 1, end = 3) is
the value a slice takes, and a type that says what a part of its values is
implements Slice for them:
type Playlist { name : String, tracks : List<String> }
impl Slice for Playlist {
fn slice(self, range: Range) : Playlist = Playlist(name = self.name, tracks = self.tracks[range])
}
mix = Playlist(name = "mix", tracks = ["first", "second", "third"])
mix[1:].tracks
["second", "third"]
The core traits are owned by the language¶
A program implements a core trait for its own types, and never for a
built-in one: impl Add for Int is a type error, since Add is already
implemented for Int in the standard library, natively. The implementations
the built-in types have are declared there as bodyless headers, and a
built-in type implements exactly what its headers name: Bool has Eq and
no Add, so true + false is a type error. A program declares no new
operator traits either; the operators of the grammar are the operators
there are.