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

Chains

Here’s something you probably haven’t had before. ## passes a value along to the next step, and _ is the value that arrived.

FUNCTION double(n)
    RETURN n * 2
END FUNCTION

FUNCTION shout(text)
    RETURN UPPER(text) + "!"
END FUNCTION

result = 4 ## double(_) ## "the answer is " + _ ## shout(_)
PRINT result
THE ANSWER IS 8!

Read it left to right: start with 4, double it, put words in front, shout it. Without the chain you’d write it inside out: shout("the answer is " + double(4)).

A chain can run over several lines, one step to a line. A statement can be nothing but a chain, too, as long as it starts by calling something.

FUNCTION only_big(list, least)
    LOCAL kept = []
    FOR item = list
        IF item >= least THEN PUSH kept, item
    END FOR
    RETURN kept
END FUNCTION

FUNCTION total(list)
    LOCAL sum = 0
    FOR item = list
        sum = sum + item
    END FOR
    RETURN sum
END FUNCTION

scores = [5, 12, 8, 30]

only_big(scores, 10)
    ## total(_)
    ## PRINT "big ones add up to", _
big ones add up to 42

Notice that _ can go anywhere in the step: first argument, second, in the middle of a sum. Any function will do, just as it was written.

Spreading the value out

So far _ has gone into the next step as one argument. Often the value coming along is really several: a pair of numbers, a set of options. That’s what the spread operator, **, is for. You met it in Functions; it earns its keep here.

**_ hands a list to the next step as separate arguments, in order.

FUNCTION area(width, height)
    RETURN width * height
END FUNCTION

FUNCTION size_of(room)
    RETURN [room.wide, room.deep]
END FUNCTION

kitchen = {wide: 3, deep: 4}

floor = kitchen ## size_of(_) ## area(**_)
PRINT floor
12

size_of gave back one list, and area wanted two numbers. **_ is the join between them, so neither function had to know about the other.

A map spreads too, into arguments by name. That makes a chain a tidy way to build up options and hand them over.

FUNCTION greet(who, greeting = "Hello", mark = "!")
    RETURN greeting + ", " + who + mark
END FUNCTION

FUNCTION excited(options)
    options.mark = "!!!"
    RETURN options
END FUNCTION

said = {greeting: "Welcome"} ## excited(_) ## greet("Ada", **_)
PRINT said
Welcome, Ada!!!

You can mix the two in one step: ordinary arguments, _ as it is, and **_ spread out.

FUNCTION total(first, rest...)
    LOCAL sum = first
    FOR value = rest
        sum = sum + value
    END FOR
    RETURN sum
END FUNCTION

sum = [1, 2, 3] ## total(100, **_)
count = [1, 2, 3] ## LEN(_)
PRINT sum, count
106 3

From Python: No more reading f(g(h(x))) from the inside out.

From JavaScript: Like method chaining with ., except the next step can be any function at all, and you choose where the value goes.

From Lua: Same idea as : chaining, without needing a method for it.

From Python: **_ is your *args and **kwargs at the call, in one operator: a list spreads in order and a map spreads by name.

From JavaScript: **_ is ...value for a list. For a map it fills parameters by name, which a spread object can’t do for you.

Crash-tan’s Tip: Every step after the first has to use _ somewhere, spread or whole. If one doesn’t, the value would be thrown away, and I’ll say so before the program runs.