Note: None of this capability is implemented as described.
A with block focuses its logic on a single value. Its sugar offers a concise way to invoke many methods on, or access fields belonging to, some value. Although it may resemble method chaining or cascades in other languages, it is more versatile.
A this block's simple mechanism consists of two aspects:
- implicit definition of a this variable within a block (not to be confused with self.)
- using operators that implicitly rely on the this variable
Implicit this Variable
A with block is simply an expression followed by a code block. Throughout the block, the variable this is understood to hold the evaluated value of that expression.
// Calculate the value of 'this' (0.174533) with Float.Pi/180 quarter = 90 * this // Use 'this' to convert to radians acute = 10 * this
The above example is short-hand for:
do imm this = Float.Pi/180 quarter = 90 * this // Use 'this' to convert to radians acute = 10 * this
Since with blocks can be nested within each other, the value of this for any statement refers to the inner-most with block's expression's value.
'.' Prefix Operator
The convenience of a with block comes from using operators which implicitly work with the value of this. The most common of these is the dot ('.') operator, used to call methods or access fields.
Normally, the '.' operator specifies some data object to the left, on which a method might be invoked or whose field is accessed. However, if no object is specified to the left of '.', this is assumed. It is common to find with block logic taking advantage of this shortcut:
// Normalize point to unit length with point imm len = (.x * .x + .y * .y).sqrt if len > 0 .x /= len .y /= len
This example uses .x and .y to access fields in point. This works because this block assigns this to the value of point, and .x is equivalent to this.x. The block's logic is cleaner and easier to read without having to place point ahead of every dot operator.
Why is this approach is more versatile than method chaining or cascades?
- Called methods need not return self, as method chaining requires.
- Method calls may appear at any point in the block logic, instead of having to be sequenced together.
- It supports field access as well as method calls.
'.' may be used to access or modify some element of a collection:
with myScene.numbers . = 1.6 // Equivalent to: myScene.numbers = 1.6 . = 1.9 .sort // Sort the list of numbers
'<-' Prefix Operator
The <- operator adds the element on the right to the collection on the left. If no collection is specified on the left, this is assumed.
// numbers is a vector of floating-point numbers with myScene.numbers <- 3.4 <- 2.6 <- 1.4 .sort
This appends three floating point numbers to the list of numbers held in myscene.numbers and then re-sorts the numbers list by calling the sort method.
Note: If we just want to add three numbers without sorting:
with myScene.numbers <- [ 3.4 2.6 1.4 ]