No longer in development
7. Let, Match, and Patterns

7. Let, Match, and Patterns

The Let-statement

You've already seen the most basic form of let:

let x = 1

This is essentially equivalent to the ES2015 code:

const x = 1;

If you're unfamiliar with const, essentially it means that the variable x cannot be assigned to again, and that any code run before that statement which tries to use x will throw an exception.

No use before initialization

Squiggle warns you if you try to use a variable before it has been initialized.

In JavaScript, this prints undefined three times.

var x = y;
var y = x;
var z = z;
console.log(x, y, z);

The following Squiggle code will also print undefined three times, but the Squiggle compiler will warn you about the error so you can take notice and correct the problem.

let x = y
let y = x
let z = z
console.log(x, y, z)

Previously Squiggle strictly enforced this at runtime by compiling variable references like foo into $ref('foo', foo), but this made the compiled JS extremely hard to read, and Babel has also moved away from this strategy.

Destructuring

The basic form of let is let name = value, but the name part can actually be shaped like an array or object, in order to pluck values from the right hand side.

Patterns

Arrays

Basically just write an array on the left, including a "..." before the last item to gobble up all remaining items. The number of items must match.

let [x, y] = [1, 2]
#=> x == 1
#=> y == 2

let [x, y] = [1]
#=> error, too few items to unpack

let [x, y] = [1, 2, 3, 4]
#=> error, too many items to unpack

let [x, ...xs] = [1, 2, 3, 4]
#=> x == 1
#=> xs == [2, 3, 4]

let [x, ...xs] = [1]
#=> x == 1
#=> x == []

let [x, ...xs] = []
#=> error, too few items to unpack

Objects

Just match the shape on the left hand side.

let {foo: foo} = {foo: 1}
#=> foo == 1

Quotes still needed for complex keys.

let {"first name": firstName} = {"first name": "Fatima"}
#=> firstName == "Fatima"

Computed values are supported within parentheses, just like object literals.

let {("a" .. "b"): q} = {ab: "x"}
#=> q == "x"

If the key and the variable have the same name, you only need to write it once.

let {name} = {name: "Ada"}
#=> name == "Ada"

Extra keys are ignored in objects.

let {x} = {x: 10, y: 47}
#=> x == 10

If a key evaluates to undefined in the object, an error is thrown.

let {x} = {y: 1}
#=> error, x undefined in object

Ignore

The special identifier _ can be used to ignore values. This is useful for ignoring values in arrays or objects.

let [_, y, _] = [1, 2, 3]
#=> y == 2

let [_, ...xs] = [4, 5, 6, 7]
#=> xs == [5, 6, 7]

let [x, ...] = [3, 9]
#=> x == 3

Note that when using a ... pattern you can omit the identifier and it's assumed to be _.

The Match-expression

The match expression is like the above destructuring assignment patterns combined with if/else.

match V
case P1 then X1
case P2 then X2
case P3 then X3
else Xn
end

In this example, V is the value you want to match on. P1, P2, and P3 are patterns. The first pattern to fully match wins, resulting in the value to the right of its then. If no patterns are matched, it throws an exception. The else clause is equivalent to case _, it will match any value.

All the pattern types from destructuring assignment are supported, in addition to matching value-based object literals: numbers, strings, true, false, undefined, null, NaN, and Infinity.

console.log(
  match "hey"
  case 56 then "This won't match!"
  case "bye" then "I never get matched..."
  case x then "You said " .. x
  end
)

You can also just supply an expression to case at the top level by wrapping it in parentheses.

let K = {
  w: "w".charCodeAt(0),
  a: "a".charCodeAt(0),
  s: "s".charCodeAt(0),
  d: "d".charCodeAt(0)
}

let dir =
  match event.which
  case (K.w) then "up"
  case (K.a) then "left"
  case (K.s) then "down"
  case (K.d) then "right"
  end

console.log(dir)