Road to Elm - Destructuring

Road to Elm - Table of Contents

Destructuring is a handy tool, available in many functional languages (also in Javascript ES6).

It's a more succinct syntax to extract single values from collections of various types.

Extracting values from objects or arrays and assigning them to local variables is one of the little things you do in programming without even thinking about it.

There's not much of a choice: littering your code with object.property.property.property or array[0] is longer to write, has slower performance, and is untidy.

Less code

In pre-ES6 Javascript that was a line by line affair:

var r1 = [9, 8];  

function area (rect){  
  var w = rect[0];
  var h = rect[1];

  return w * h;
}

In Elm, it would look like this:

r1 = (9, 8)

addAreas : (Int, Int) -> Int  
area rect =  
  let
    (w, h) = rect -- using destructuring
  in 
    w * h

So we exchange two lines:

var w = rect[0];
var h = rect[1];

for one

(w, h) = rect

and we end up with the same result, w is 9, and h is 8.

If we had something longer, like:

var list = [2, 5, 6, 7, 8];

var x = list[0];
var y = list[1];
var z = list[2];
var a = list[3];
var b = list[4];

it would still be one line in Elm:

list = (2,5,6,7,8)

(x, y, z, a, b) = list

Skipping

or maybe you want only x, a, and b:

list = (2,5,6,7,8)
(x, _, _, a, b) = list

_ will be ignored.

Objects/Records example

So we looked at tuples (Elm) and arrays (Javascript), now let's do the same for records (Elm) and objects (Javascript).

Objects and records are not the same, but they are similar enough for our purposes in this article, much like tuples and arrays.

var r1 = { x : 9, y : 8 };  

function area (rect) {  
  var w = rect.x;
  var h = rect.y;

  return w * h;
}

the same in Elm:

type alias Rect
  = { x : Int
    , y : Int
    }

area : Rect -> Int  
area rect =  
  let
    { x, y } = rect -- using destructuring
  in 
    x * y

you can also use it in place of the argument name:

area : Rect -> Int  
area { x, y } =  
  x * y

this way you skipped even the let, but notice that it was necessary to use x and y, matching the Rect type definition, instead of being able to use different letters, as w and h in the let above.

Some fancier examples

Nested destructuring

You can also extract values from a tuple inside a tuple:

tryNested ( x,( w, h ), g ) =
  w

> tryNested (5, (6,7), 3)
6 : number

or a record inside a tuple:

tryNested ( x, { w, h }, g ) =
   w

> tryNested ( 3, { w=3, h=5 }, 6)
3 : number

Use within case

You can destructure a record from the arguments and then use it in a case...of within a tuple:

tryCase { x, y } =
  case (x, y) of
    (5,5) -> "Correct"
    (_,3) -> "Less correct"
    _ -> "Wrong"

which we can try out in elm-repl:

> tryCase {x = 5, y = 7}
"Wrong" : String

> tryCase {x = 5, y = 5}
"Correct" : String

> tryCase {x = 4, y = 3}
"Less correct" : String

Reminder

In a function always assign your variables within a let!