Operators

Corporate accounts payable, Nina speaking! Just a moment!

—Nina, corporate accounts payable, Office Space

The expression subset of the Monte grammar is presented here in operator precedence order, meaning that later constructs bind tighter than earlier constructs. For example, expr “+” expr is presented before expr “*” expr, so * binds tighter than +. Therefore, a + b * c + d is equivalent to a + (b * c) + d. All the constructs in presented in the same section have the same precedence.

Monte has a rich set of operators above and beyond those in Kernel-Monte. All operators are overloadable, but overloading follows a very simple set of rules: Operators expand to message passing, and the message is generally passed to the left-hand operand, except for a few cases where the message is passed to a helper object which implements the operation. In object capability shorthand, we are asking the object on the left what it thinks of the object on the right.

There are some special rules about the behavior of the basic operators because of E’s distributed security.

Todo

special operator rules because of security

Sequence

sequence

blockExpr expr ;

A sequence expressions evaluates to the value of its last item:

>>> { 4; "x"; "y" }
"y"

Assignment and Definition

assign

def pattern exit order := assign VarPatt BindPatt := assign lval := assign VerbAssignExpr order

lval

order [ expr , ] name

Assignment is right associative. The list update on the right happens before the definition on the left:

>>> def color := ["red", "green", "blue"].diverge()
... def c := color[1] := "yellow"
... c
"yellow"

Indexed Update Expansion

An indexed update expands to a call to put:

>>> m`x[i] := 1`.expand()
m`x.put(i, def ares_1 := 1); ares_1`

Augmented Assignment Expansion

VerbAssignExpr

lval VERB_ASSIGN assign

All binary operators which pass a message to the left-hand operand can be used as augmented assignment operators. For example, augmented addition is legal:

>>> { var x := "augmenting "; x += "addition!"; x }
"augmenting addition!"

Behind the scenes, the compiler transforms augmented operators:

>>> m`x += "addition!"`.expand()
m`x := x.add("addition!")`

Monte permits this augmented construction for any verb, not just those used by operators. For example, the with verb of lists can be used to incrementally build a list:

>>> { var l := []; for i in (1..10) { l with= (i) }; l }
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

And even non-unary messages can get in on the fun, with a properly placed pair of parentheses:

>>> { var x := 7; x modPow= (129, 3) }
1

Todo

VERB_ASSIGN lexical details

Assignment operators

>>> var x := 5; [ x += 2, x -= 1, x *= 2, x **= 3 ]
[7, 6, 12, 1728]
>>> var x := 50; [ x //= 3, x %= 7, x /= 4]
[16, 2, 0.500000]
>>> var x := 5; [ x ^= 3, x |= 15, x &= 7, x <<= 3, x >>= 2]
[6, 15, 7, 56, 14]

Conditional-Or

logical_or

logical_and || logical_or

Monte uses C syntax for the basic logical operators:

>>> false || true
true

Evaluates left to right until it finds a true condition.

>>> {((1 =~ x) || (2 =~ x)); x}
1
>>> {((1 =~ [x, y]) || (2 =~ x)); x}
2

Conditional-And

logical_and

comp && logical_and

Logical Expansion

Boolean conditionals expand to if expressions:

>>> m`a || b`.expand()
m`if (a) { true } else if (b) { true } else { false }`

>>> m`a && b`.expand()
m`if (a) { if (b) { true } else { false } } else { false }`

Comparisons and Bitwise/Logical Operators

comp

order =~ !~ == != &! ^ & | comp order

order

CompareExpr RangeExpr BinaryExpr prefix

These are non-associative: x == y == z is a syntax error.

>>> false == true
false
>>> false != true
true

You can compare with a pattern and use the resulting bindings:

>>> [1, "x"] =~ [_ :Int, _ :Str]
true

>>> [1, 2] =~ [a, b]; b
2

>>> "<p>" =~ `<@tag>`; tag
"p"

>>> "<p>" !~ `</@tag>`
true

Comparison is more strict than you might expect:

>>> 3 == "3"
false

>>> 1 + 1 == 2.0
false

We also have negated implication operator:

>>> true &! false
true

Boolean Comparisons (non-associative):

>>> false & true
false

>>> false | true
true

>>> false ^ true
true

Comparison Expansion

Comparisons expand to use of a helper object:

::
>>> m`x == y`.expand()
m`_equalizer.sameEver(x, y)`
>>> m`x != y`.expand()
m`_equalizer.sameEver(x, y).not()`
>>> m`"value" =~ pattern`.expand()
m`def sp_1 := "value"; def [ok_2, &&pattern] := escape fail_3 { def pattern exit fail_3 := sp_1; _makeList.run(true, &&pattern) } catch problem_4 { def via (_slotToBinding) &&broken_5 := Ref.broken(problem_4); _makeList.run(false, &&broken_5) }; ok_2`
>>> m`"value" !~ pattern`.expand()
m`(def sp_1 := "value"; def [ok_2, &&pattern] := escape fail_3 { def pattern exit fail_3 := sp_1; _makeList.run(true, &&pattern) } catch problem_4 { def via (_slotToBinding) &&broken_5 := Ref.broken(problem_4); _makeList.run(false, &&broken_5) }; ok_2).not()`
>>> m`x ^ y`.expand()
m`x.xor(y)`
>>> m`x & y`.expand()
m`x.and(y)`
>>> m`x | y`.expand()
m`x.or(y)`
>>> m`x &! y`.expand()
m`x.butNot(y)`

Partial Ordering

CompareExpr

prefix > < >= <= <=> order

Monte has the usual ordering operators:

>>> 3 < 2
false
>>> 3 > 2
true
>>> 3 < 3
false
>>> 3 <= 3
true

They are non-associative and only partial:

>>> try { 3 < "3" } catch _ { "ouch! no order defined" }
"ouch! no order defined"

Use <=> aka asBigAs to compare magnitudes:

>>> 2.0 <=> 1 + 1
true

>>> 2 + 1 <=> 3.0
true

Ordering Expansion

Ordering operators expand to use of a helper object:

>>> m`3 < 2`.expand()
m`_comparer.lessThan(3, 2)`

>>> m`2.0 <=> 1 + 1`.expand()
m`_comparer.asBigAs(2.000000, 1.add(1))`

Interval

RangeExpr

prefix .. ..! order

Non-associative.

We can build a half-open interval with the range operator:

>>> [for x in (1..!4) x * 2]
[2, 4, 6]

Or we can build closed intervals with the inclusive range operator:

>>> [for x in (1..4) x * 2]
[2, 4, 6, 8]

Half-open intervals are more typical, though they are in most ways equivalent to closed intervals:

>>> (0..!10) <=> (0..9)
true

Expansion:

>>> m`lo..hi`.expand()
m`_makeOrderedSpace.op__thru(lo, hi)`

>>> m`lo..!hi`.expand()
m`_makeOrderedSpace.op__till(lo, hi)`

Shift

shift

prefix << >> order

Left associative.

Among built-in data types, this is only defined on integers, and has the traditional meaning but with no precision limit.

Expansion:

>>> m`i << bits`.expand()
m`i.shiftLeft(bits)`

>>> m`i >> bits`.expand()
m`i.shiftRight(bits)`

Additive

additiveExpr

multiplicativeExpr + - additiveExpr

Left associative.

::
>>> [1, 2] + [3, 4]
[1, 2, 3, 4]
>>> "abc" + "def"
"abcdef"
>>> ["square" => 4] | ["triangle" => 3]
["square" => 4, "triangle" => 3]
>>> def sides := ["square" => 4, "triangle" => 3]
... sides.without("square")
["triangle" => 3]

Expansion:

>>> m`x + y`.expand()
m`x.add(y)`

>>> m`x - y`.expand()
m`x.subtract(y)`

Multiplicative

multiplicativeExpr

exponentiationExpr * / // % order

Left associative.

>>> 2 * 3
6

Modular exponentiation:

>>> 5 ** 3 % 13
8

expansion:

>>> m`base ** exp % mod`.expand()
m`base.modPow(exp, mod)`

Exponentiation

exponentiationExpr

prefix ** order

Non-associative.

>>> 2 ** 3
8

Expansion:

>>> m`2 ** 3`.expand()
m`2.pow(3)`

Unary Prefix

prefix

- prim ~ ! calls SlotExpr BindingExpr CoerceExpr calls

SlotExpr

& name

BindingExpr

&& name

Monte has logical, bitwise, and arithmetic negation operators:

>>> - (1 + 3)
-4
>>> ~ 0xff
-256
>>> ! true
false

Todo

discuss, doctest SlotExpression &x, BindingExpression &&x

Expansions:

>>> m`! false`.expand()
m`false.not()`

Unary Postfix

MetaExpr

meta . context ( ) getState ( )

CoerceExpr

calls : guard
meta.getState()
meta.context()

A guard can be used as an operator to coerce a value:

>>> 1 :Int
1

Call

calls

prim call send index curryTail

call

. verb argList

send

<- verb argList

curryTail

. verb <- verb

index

[ expr , ]

verb

IDENTIFIER .String.

argList

( expr , )

Todo

named args in argList

There are two ways to pass a message. First, the immediate call:

>>> { def x := 2; def result := x.add(3) }
5

And, second, the eventual send:

>>> { def x; def prom := x<-message(3); null }
null

Calls may be curried:

>>> { def x := 2; def xplus := x.add; xplus(4) }
6

Todo

discuss matchers in object expressions

Call Expansion

Function call syntax elaborates to a call to run ( and likewise vice-versa):

>>> m`f(x)`.expand()
m`f.run(x)`

Indexing elaborates to a call to get:

>>> { object parity { to get(n) { return n % 2 }}; parity[3] }
1

M is a helper object that provides several runtime services. It can pass messages on behalf of other objects and quote strings.

Eventual send syntax expands to calls to M:

>>> m`target<-verb(args)`.expand()
m`M.send(target, "verb", _makeList.run(args), _makeMap.fromPairs(_makeList.run()))`

>>> m`target<-verb(args, "name" := namedArg)`.expand()
m`M.send(target, "verb", _makeList.run(args), _makeMap.fromPairs(_makeList.run()))`