Browse the docs

Operators.

Silk has eleven binary operator levels, one prefix level and one postfix level. The table runs from weakest to strongest and comes from the parser's precedence table. Each overloadable operator is bound to one interface; silk/core/ops, silk/core/equal and silk/core/order name them.

Precedence#

LevelOperatorsAssociativity
1|>left
2||left
3&&left
4== !=none
5< <= > >=none
6|left
7^left
8&left
9+ -left
10* / %left
11prefix - ! ~ & &mut move unsaferight
12postfix call f(x), f<T>(x), .name, .0, .*, [i]left
  • run is a prefix too, but it takes the whole rest of the expression, pipelines included: run f() != 4 is run (f() != 4). Write (run f()) != 4.

  • Bitwise operators bind tighter than comparisons, so x & 1 == 1 is (x & 1) == 1.

  • Comparison and equality do not chain. 1 < 2 < 3 is a parse error at the second operator:

TEXT
src/main.silk:2:17: error[PAR0002] Unexpected `<`; expected valid syntax
src/main.silk:2:19: error[SEM0087] Expression statement produces i32, but only () or never may be ignored
  • An index [i] must start on the same line as its operand. A call, .name or |> may start on the next line.

  • = is not an operator. Assignment is a statement, and there is no compound assignment (+=).

  • There are no shift operators, no ** and no cast operator. 1 << 2 is PAR0002. Use i32.shiftLeft, i32.toI64 and similar functions.

Operator interfaces#

OperatorInterface (operation)Built in for
a + bAdd<Right, Output> (add)same integer type, same float type
a - bSubtract<Right, Output> (subtract)same integer type, same float type
a * bMultiply<Right, Output> (multiply)same integer type, same float type
a / bDivide<Right, Output> (divide)same integer type, same float type
a % bRemainder<Right, Output> (remainder)same integer type, same float type
-aNegate<Output> (negate)signed integers, floats
a & bBitAnd<Right, Output> (bitAnd)same integer type
a | bBitOr<Right, Output> (bitOr)same integer type
a ^ bBitXor<Right, Output> (bitXor)same integer type
~aBitNot<Output> (bitNot)integers
!aNot<Output> (not)bool
&a == &b, &a != &bEqual (equals)integers, floats, bool, char, string, scalar enums (by value)
&a < &b, <=, >, >=Order (compare)integers, floats, char (by value)
a && b, a || bnonebool only, short-circuit
x |> f(y)nonecalls f(x, y)
  • The interfaces live in the Intrinsic namespace. silk/core/ops exports Add, Subtract, Multiply, Divide, Remainder, Negate, BitAnd, BitOr, BitXor, BitNot and Not. silk/core/equal exports Equal; silk/core/order exports Order and Ordering.

  • Arithmetic, bitwise, Negate, BitNot and Not take their operands by value. Equal and Order take &Self, so a user type compares through references: &a == &b.

  • != is the negation of equals. < tests compare == Less, <= tests != Greater, > tests == Greater, >= tests != Less.

  • Right and Output let one type combine with another, as in impl Multiply<f64, Vec2> for Vec2.

  • Concrete primitive operands always use the built-in operator. Both operands must have the same type; there is no implicit widening.

  • No import turns an operator on. The conformance must be declared in the module that declares the type.

  • No matching conformance is SEM0135; two matching conformances are SEM0136.

TEXT
src/main.silk:4:13: error[SEM0135] Operator + does not accept (i64, i32)
src/main.silk:2:11: error[SEM0135] Operator ! does not accept (i32)
src/main.silk:2:16: error[SEM0135] Operator & does not accept (bool, bool)

Generic code and primitives#

A bound such as T: Add<T, T> lets generic code use an operator. A primitive type satisfies an operator bound when its built-in operator accepts it:

BoundPrimitive types that satisfy it
Add, Subtract, Multiply, Divide, Remainderthe integer types, f32, f64
Negatethe signed integer types, f32, f64
BitAnd, BitOr, BitXor, BitNotthe integer types
Notbool
Equalthe integer types, f32, f64, bool, char
Orderthe integer types, char

Floats compare with < but do not satisfy Order. Any other bound on a primitive is SEM0121 at the call, for example T: Subtract<T, T> with bool:

TEXT
src/main.silk:4:14: error[SEM0121] `bool: intrinsic/operators.Subtract<bool, bool>` cannot be proved: no implementation of `intrinsic/operators.Subtract<bool, bool>` exists for `bool`

Example#

Every overloadable operator on one user type. The program exits 0.

silk
import silk.core.ops {
  Add,
  Subtract,
  Multiply,
  Divide,
  Remainder,
  Negate,
  BitAnd,
  BitOr,
  BitXor,
  BitNot,
  Not
}
import silk.core.equal { Equal }
import silk.core.order { Order, Ordering }

struct N {
  v: i32,
}

fn add(a: N, b: N) -> N {
  return N { v: a.v + b.v }
}

fn sub(a: N, b: N) -> N {
  return N { v: a.v - b.v }
}

fn mul(a: N, b: N) -> N {
  return N { v: a.v * b.v }
}

fn div(a: N, b: N) -> N {
  return N { v: a.v / b.v }
}

fn rem(a: N, b: N) -> N {
  return N { v: a.v % b.v }
}

fn neg(a: N) -> N {
  return N { v: -a.v }
}

fn band(a: N, b: N) -> N {
  return N { v: a.v & b.v }
}

fn bor(a: N, b: N) -> N {
  return N { v: a.v | b.v }
}

fn bxor(a: N, b: N) -> N {
  return N { v: a.v ^ b.v }
}

fn bnot(a: N) -> N {
  return N { v: ~a.v }
}

fn lnot(a: N) -> bool {
  return a.v == 0
}

fn eq(a: &N, b: &N) -> bool {
  return a.v == b.v
}

fn cmp(a: &N, b: &N) -> Ordering {
  if a.v < b.v {
    return Ordering.Less
  }
  if a.v > b.v {
    return Ordering.Greater
  }
  return Ordering.Equal
}

impl Add<N, N> for N {
  add: N.add
}

impl Subtract<N, N> for N {
  subtract: N.sub
}

impl Multiply<N, N> for N {
  multiply: N.mul
}

impl Divide<N, N> for N {
  divide: N.div
}

impl Remainder<N, N> for N {
  remainder: N.rem
}

impl Negate<N> for N {
  negate: N.neg
}

impl BitAnd<N, N> for N {
  bitAnd: N.band
}

impl BitOr<N, N> for N {
  bitOr: N.bor
}

impl BitXor<N, N> for N {
  bitXor: N.bxor
}

impl BitNot<N> for N {
  bitNot: N.bnot
}

impl Not<bool> for N {
  not: N.lnot
}

impl Equal for N {
  equals: N.eq
}

impl Order for N {
  compare: N.cmp
}

pub fn main() -> i32 {
  let a = N { v: 12 } + N { v: 3 } - N { v: 1 }
  let b = N { v: 2 } * N { v: 7 } / N { v: 2 } % N { v: 4 }
  let c = -N { v: 5 }
  let d = (N { v: 6 } & N { v: 3 }) | (N { v: 8 } ^ N { v: 1 })
  let e = ~N { v: 0 }
  let f = !N { v: 0 }
  if a.v != 14 || b.v != 3 || c.v != -5 || d.v != 11 || e.v != -1 || !f {
    return 1
  }
  let one = N { v: 1 }
  let two = N { v: 2 }
  if !(&one == &one) || &one != &one {
    return 2
  }
  if !(&one < &two) || &two <= &one || &one > &two || !(&two >= &two) {
    return 3
  }
  return 0
}

See also#

Expressions and operators, Interfaces and impls, Generics, Keywords. Modules: silk/core/ops, silk/core/equal, silk/core/order, silk/num/numeric.

View the source of this page