ERights Home elang / blocks 
Back to: Iterating by Key-Value Pairs On to: Defining Functions

Defining Variables


def pattern := init-expression
The most common use of this construct just has a variable name as the pattern:
? pragma.syntax("0.8")

? def i := 2 + 3
# value: 5

As should be familiar, the value of the init-expression becomes the initial value of the variable.

E adds power to this construct, not by adding new features, but by removing three restrictions. In E,

  1. Definitions are also expressions with values
  2. Any pattern can appear as the left hand side
  3. Variables defined in the left are in scope on the right

Definitions Are Also Expressions

The define expression has a value -- the value of the init-expression. Thus enabling idioms like

while ((def c := reader.readChar()) != null) {

Definition By Pattern Match

Besides a simple variable name, the pattern on the left can be any pattern. This pattern is matched against the value of the init-expression. If this match fails, a ***to be specified*** exception is thrown, causing us to leave this scope. If the match succeeds, execution continues sequentially, and all the variable definitions produced by the pattern match are in scope. Most commonly, this is used when calling a function that effectively returns multiple results.

For example, in the Concurrency Chapter, we'll encounter a function Ref.promise() that returns a two element list containing two related values. The zero element of this list is a Promise, and the one element is a corresponding Resolver. We might first think to write the following E code to receive these two values

? def pair := Ref.promise()
# value: [<Promise>, <Resolver>]
? def promise := pair[0]
# value: <Promise>
? def resolver := pair[1]
# value: <Resolver>

While this works, it's clearer and more concise if you just use a List-pattern on the left

? def [promise, resolver] := Ref.promise()
# value: [<Promise>, <Resolver>]
? promise
# value: <Promise>
? resolver
# value: <Resolver>

The List-pattern on the left extracts the elements of the list returned by Ref.promise(), and defines the two variables, promise and resolver to hold these values.

More conventionally, pattern matching can be used to define typed variables

? var i :int := 2 + 3
# value: 5
? var j :char := 2 + 3
# problem: <ClassCastException: Integer \
#           doesn't coerce to a Character>

? def k :int := 2 + 3
# value: 5

? i := 7
# value: 7

? k := 7
# problem: Failed: Can't assign to final variable: k

"def" defines an immutable variable ("final" in Java terminology). This means that the variable's initial value is also its final value -- that it may not be reassigned. "var" defines a mutable variable -- one that can be assigned to. See Pattern Matching for the full set of possibilities.

Consistent Scopes Enable Cyclic Initialization

As is usually true in E, the scope of any variable names defined by the pattern on the left lasts left-to-right until the end of the enclosing scope box. In E the init-expression is not exempt from this rule -- the variables defined on the left are available on the right. This allows the definition of cyclic data structures:

? def a := [1, a, 3]
# value: [1, <***CYCLE***>, 3]
? a[1][1][1][0]
# value: 1

This defines "a" to be the infinite tree of lists:

[1, [1, [1, ..., 3], 3], 3]

which is implemented by the obvious cyclic data structure. Though this example is simple but silly, the same technique allows us to naturally create a set of objects that need to be initialized to point at each other. A classic example is the Smalltalk model-view-controller triple. The model is born not knowing about the view or controller, but the view needs to know the model and the controller, and the controller needs to know the model and the view. If our three construction functions are makeModel, makeView, and makeController, we can construct our three objects as follows

def model := makeModel(...)
def [view, controller] := [
    makeView(model, controller),
    makeController(model, view)
]

***show the expansion of cyclic definitions to non-cyclic definitions.

 
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.
ERights Home elang / blocks 
Back to: Iterating by Key-Value Pairs On to: Defining Functions
Download    FAQ    API    Mail Archive    Donate

report bug (including invalid html)

Golden Key Campaign Blue Ribbon Campaign