Skip to content

Extract Destructuring

Shane Brinkman-Davis Delamore edited this page Jun 18, 2018 · 6 revisions

See also: Destructuring, Structuring and Restructuring

CaffeineScript supports two destructuring syntaxes:

  1. Extract: a extract b. Extract generally is more capable and uses less syntax. Extract returns the last value extracted.
  2. Assignment: {b} = a`. Assignment-style's return-value follows supports Restructuring.

Assignment-style will feel familiar to JavaScript and CoffeeScript veterans. With the exception of the return-value, assignment-style is identical to JavaScript ES6-style , and is a subset of CoffeeScript-Style-Destructuring. Extract, when complete, will be support everything CoffeeScript supported and more.

Beyond JavaScript-Style Destructuring

CoffeeScript and Javascript, as of EcmaScript6 (ES6), have "destructuring assignment." At first it seems cool and elegant, but after using it for a few years, I've found it awkward and largely ineffective in all but the simplest cases:

  • Anything more than trivial [] and {} CoffeeScript/JavaScript-pattern-assignment generally isn't worth it.
    • It isn't any more token-efficient than non-pattern assignment.
    • It is hard to read, since data flow is going right-to-left initially, and then left-to-right.
  • CoffeeScript/JavaScript destructuring returns the object being destructured. I have never found that useful. Instead, I usually want the extracted value returned, usually for testing in an if-statement if it is present.
  • It requires a lot of {} and [] bracket-matching.

Extract

Introducing extract. Key features:

  • Data-flow direction is always the same direction: left-to-right
  • Conditional extraction
  • Returns the last value extracted - as long as all values could be extracted.
  • Reduced tokens
  • Pathed extraction

Example:

# CaffeineScript - 6 tokens
compile() extract compiled extract js
# OR
compile() extract compiled.js

# CoffeeScript - 10 tokens
{compiled:{js}} = compile()
# Or, without pattern assignment, CoffeeScript is actually shorter - 8 tokens
js = compile().compiled.js

Object Extraction

# CaffeineScript - 7 tokens
Engine extract Elements extract Base, Bitmap
# Or, 6 tokens
Engine extract Elements extract
  Base
  Bitmap

# CoffeeScript - 10 tokens
{Elements:{Base, Bitmap}} = Engine
# Or, 12 tokens
{Elements} = Engine
{Base, Bitmap} = Elements

Array Extraction

# CaffeineScript - 8 tokens
myArray extract [] a, b, c

# CaffeineScript alt - 6 tokens
myArray extract []
  a
  b
  c

# CoffeeScript - 9 tokens
[a, b, c] = myArray

Conditional Extraction

# CaffeineScript - 3 tokens
Engine extract? Elements

# CoffeeScript - 7 tokens
{Elements} = Engine if Engine

Nested Conditional Extraction

# CaffeineScript - 7 tokens
Engine extract? Elements extract? Base, Bitmap

# CoffeeScript - 15 tokens
{Elements:{Base, Bitmap}} = Engine if Engine?.Elements

Pathed Extraction

The old way isn't too bad if you are just pathing the input object:

# CaffeineScript - 5 tokens
Engine.Elements extract Base

# CoffeeScript - 7 tokens
{Base} = Engine.Elements

But when you get just a little more complex, the extract Syntax remains elegant while CoffeeScript/JavaScript starts to get ugly.

# CaffeineScript - 7 tokens
Engine extract Core, Elements.Base

# CoffeeScript - 10 tokens
{Core, Elements:{Base}} = Engine

Pathing and Refactoring

Consider two lines of CaffeineScript vs CoffeeScript. Each pair is logically the same before/after of a simple refactor. The CaffeineScript refactor effort is trivial, while the CoffeeScript effort is complex and error prone. The goal of the refactor is to extract Core from Engine in addition to the Elements.

# CaffeineScript
Engine.Elements               extract Base, Bitmap # before
Engine extract Core, Elements extract Base, Bitmap # after

# CoffeeScript
                {Base, Bitmap}  = Engine.Elements # before
{Core, Elements:{Base, Bitmap}} = Engine          # after

Default Extraction

# CaffeineScript - 10 tokens
Engine extract? Elements extract?
  Base = default1
  Bitmap = default2

# CoffeeScript - 19 tokens
{Elements:{Base = default1, Bitmap = default2}} = Engine if Engine?.Elements

Function Argument Extraction with Defaults

# CaffeineScript - 7 tokens
(extract? a, b) ->

# CoffeeScript - 13 tokens
(options = {}) ->
  {a, b} = options

Function Argument Extraction with Defaults AND Unextracted Argument Capture

A constant frustration of mine with CoffeeScript is I want to extract some values from an argument, but I also need access to that argument later. There is no good way to do this in CoffeeScript or JavaScript. By separating 'extract' from normal assignment, there is no longer a syntax ambiguity when we want to end up with defaults, extraction and named arguments:

# CaffeineScript - 14 tokens
(options = {} extract a = 1, b = 2) ->
# OR 12 tokens
(options extract? a = 1, b = 2) ->

# CoffeeScript - 17 tokens
(options = {}) ->
  {a = 1, b = 2} = options

Extract's Return-Value

Used in an expression, extract returns all the extracted values &&ed together in order. Effectively:

  • if all values could be extracted, it returns the last one
  • else it returns the first false-ish value
# CafScript
if MyLib extract foo bar baz
  foo bar baz()
else throw new Error "Expected MyLib to have foo, bar and baz!"
# CafScript
if MyLib?.foo extract? boo
  boo()
else throw new Error "Failed to find MyLib.foo.boo!"

Works like other binary operators

# CaffeineScript - 4 tokens
... # long, complex expression
extract a, b

# CoffeeScript - 6 tokens
{a, b} = ... # long, complex expression
Clone this wiki locally