Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Undefined values

When an operation makes no sense, such as subtracting a number from a text, Swan does not stop. The operation returns an Undefined value, which records what went wrong, and the evaluation continues.

"hello" - 1          # => Undefined
type ("hello" - 1)   # => "Undefined"

In a template, an undefined value renders as [[Undefined]].

Where they come from

  • Operators applied to the wrong types: "a" + 1, -"a", TRUE < 1.
  • Applying a value that cannot be applied: 5 3.
  • Built-in functions given the wrong arguments: text.size 42.
  • Math results that are not numbers: math.sqrt (-1).
  • Syntax errors: (1 + 2, 1 + * 2.
  • Binding something that is not a name: 1 = 2.
  • undef.from, which creates one on purpose.

An unbound name is not undefined: it is Nothing.

How they spread

Most operations with an undefined operand give an undefined result, so an error early in an expression usually shows in its final value:

("hello" - 1) + 5    # => Undefined
text.upper ("hello" - 1)       # => Undefined

A few operations handle them like other values:

  • An undefined value is false, so ?, &, | and ! treat it as such.
  • ; does not replace it, because it is not Nothing.
  • A tuple keeps it as one of its items.
!("hello" - 1)       # => TRUE
("hello" - 1) | 0    # => 0
("hello" - 1) ; 0    # => Undefined
(1, "a" - 1, 3)      # => (1, Undefined, 3)

So | is the way to replace an undefined value with a default.

Finding out what went wrong

undef.inspect turns an undefined value into a namespace that describes it:

err = undef.inspect ("hello" - 1)
err.type             # => "BinaryOperation"
err.op               # => "subtraction"
err.lhv              # => "hello"
err.rhv              # => 1
typeCauseOther members
UnaryOperationa prefix operator or a built-in functionop, val, pos
BinaryOperationa binary operator or an applicationop, lhv, rhv, pos
Terminationa missing closing bracketexpected, pos
Tokena character or token in the wrong placetoken, pos
Namea name was expectedpos
Valuecreated with undef.fromval

op names the operation, such as "sum", "application" or "text.size". pos is the position in the expression where the error happened, counted in bytes from the start of the expression (in a template, from the start of the tag's content). Built-in functions report position 0. When an operand is itself undefined, undef.inspect describes it too, so you can follow the chain back to the original error.

Creating undefined values

A function can return an undefined value to signal that its input was invalid. undef.from wraps any value, such as a message, into one:

safe_div = (a, b) -> b == 0 ? undef.from "division by zero" ; a / b
safe_div (1, 0)      # => Undefined
(undef.inspect (safe_div (1, 0))).val    # => "division by zero"