It's about Iterables and Iterators
Garrett Dawson
@killtheliterate
I've been a front-end dev of 5 years. I work at VictorOps
I love JavaScript
What will people discover with es2015 onwards?
Ada Lovelace and the Analytical Engine
* Loops began with Ada Lovelace. Bernoulli numbers and the analytical engine. * This wasn't software, but it was programming. * https://programmers.stackexchange.com/questions/149465/who-created-the-ideas-of-the-first-loop-constructs****** Algol 60 ****** FOR i:=1 UNTIL 5 DO FOR j:=1 UNTIL i DO OUTTEXT("*"); OUTLINE
* COBOL IDENTIFICATION DIVISION. PROGRAM-ID. Display-Triangle. DATA DIVISION. WORKING-STORAGE SECTION. 01 Outer-Counter PIC 9. 01 Inner-Counter PIC 9. PROCEDURE DIVISION. PERFORM VARYING Outer-Counter FROM 1 BY 1 UNTIL 5 < Outer-Counter PERFORM VARYING Inner-Counter FROM 1 BY 1 UNTIL Outer-Counter < Inner-Counter DISPLAY "*" NO ADVANCING END-PERFORM DISPLAY "" *> Output a newline END-PERFORM GOBACK .
'Visual Basic Public OutConsole As Scripting.TextStream For i = 0 To 4 For j = 0 To i OutConsole.Write "*" Next j OutConsole.WriteLine Next i
for
// loopable const theThing = ['hello', 'humans', 'in', 'this', 'room'] // the loop for(let i = 0; i < theThing.length; i = i + 1) { console.log(theThing[i]) // hello, humans... }counter Ancient and venerated, Similar to many other langs Have to track STATE of the loop, terminated at CONDITION Obviously, problems! STATE We have to be very verbose about what's happening in our loop
for-in
// loopable const otherThing = ['hello', 'humans', 'in', 'this', 'room'] // the loop for(let i in otherThing) { console.log(otherThing[i]) // hello, humans... }we use the counter to access the loopable Higher level, no state Less how, more what
...iterate on all enumerable properties
// loopable const theCollection = ['hello', 'humans', 'in', 'this', 'room'] // shwhoops theCollection.alsoEnumerable = 'enumerated' // the loop for(let i in otherThing) { console.log(otherThing[i]) // hello, humans... enumerated }the collection types we're used to don't distinguish for-in enumerates all props on object which is to say, it enumerates all enumerable properties scope continues to be a problem
Array#applicatives
// loopable const say = ['hello', 'humans', 'in', 'this', 'room'] // weird... say.justLeavingThisHere = 'boo' // the loop otherThing.forEach(el => console.log(el)) // hello, humans...And of course, we have map, filter and reduce Only arrays, strictness
// loopable const theIterable = [1,2,3] // the loop theIterable.forEach(el => console.log(el)) // 1, 2, 3Given these commonalities, TC39 identifies Iterable/Iterator
Builtin Iterators
// an iterable const theIterable = 'a string is a list of characters' // an iterator // access the member directly // instead of by index for(let char of theIterable) { console.log(char) }Looks similar to for-in access member directly
// an iterable const theIterable = ['an', 'array', 'is', 'iterable'] // an iterator const newArray = [...theIterable] // arguments is iterable const fn = (...args) => { args.forEach(el => console.log(el)) } fn(1, 2, 3)Looks alot like [].map() The arguments object can be passed to an iterator
// an iterable const theCollection = ['hello', 'humans', 'in', 'this', 'room'] // an iterator let [these, are, pieces, ...iterable] = theCollection console.log(these) // 'hello' console.log(iterable) // ['this', 'room']Also looks alot like [].map() Composable with ...spread to collect the rest Notably, objects can be destructured, but that's outside this scope
The future is in the future
// array comprehension const newArray = [for (i of [ 1, 2, 3 ]) i * i ] console.log(newArray) // 1, 4, 9 // generator comprehension const newGen = (for (i of [ 1, 2, 3 ]) i * i ) console.log(newGen.next()) // {value: 1, done: false} console.log(newGen.next()) // {value: 4, done: false}I can't wait, Also like [].map() Another way to extract pieces of a list Check it out in the Babel REPL
Builtin Iterables
// an iterable const theIterable = 'this is iterable' // an iterator for(let char of theIterable) { console.log(char) // each char } // an iterator const chars = [...theIterable] console.log(chars) // ['t','h','i','s',' '...]Since they are iterable, they are consumable by iterators
// an iterable const theIterable = ['this', 'is', 'iterable'] // an iterator for(let words of theIterable) { console.log(words) // 'this' 'is'... } // an iterator const sentence = [...theIterable] console.log(sentence) // ['this', 'is', 'iterable']
// an iterable const theIterable = new Set() theIterable.add(1) theIterable.add(3) theIterable.add(2) theIterable.add(2) // an iterator for(let num of theIterable) { console.log(num) // 1, 3, 2 } // an iterator const numbers = [...theIterable] console.log(numbers) // [1,3,2]
// an iterable const theIterable = new Map() theIterable.set('one', 1) theIterable.set('three', 3) theIterable.set('two', 2) // an iterator for(let numTuple of theIterable) { console.log(numTuple) // ['one', 1]... } // an iterator const tuples = [...theIterable] console.log(tuples) // [['one', 1]...]
// an iterable const maker = function* () { yield 1 yield 2 yield 3 } const theIterable = maker() // this gives us back a generator object const first = theIterable.next() // which we can step through console.log(first) // {value: 1, done: false}yield keyword exits the function execution resumes where the yield left off
// an iterable const maker = function* () { let index = 0 // the mutated state will be persisted while(index < 3) { yield index++ } } const theIterable = maker() // this gives us back a generator object // which we can operate on with an iterator // this works because the generator is finite const nums = [...theIterable] // [0,1,2]It's notable that using spread transforms the values into normal stuff
const maker = function* () { let first = yield let second = yield first let third = yield second yield } const echo = maker() console.log(echo.next(1))// {value: null, done: false} console.log(echo.next(500)) // {value: 500, done: false} console.log(echo.next('sup')) // {value: 'sup', done: false}We can push values into Generators If we assign the last yield to a variable, that variable becomes whatever we push with next()
const anIterable = 'I am iterable' const also = ['so', 'am', 'i'] for(i of anIterable) { console.log(i) } for(i of also) { console.log(i) }If it is an Iterable, you can consume it with an iterator We can codify these patterns in protocols
{ }.next()
An object is an iterator when it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence "how do i loop?" this is why a generator is both iterable and iterator...for a linked list
http://bit.ly/1OVllSX
// this is what a linked list looks like {val: 1, rest: {val: 2, rest: {val: 3, rest: {val: 4, rest: null}}}} // basically an array [1, 2, 3, 4]quick aside about linked lists why are they useful? memory and known size collection types in FP are usually linked lists you walk them recursively
[Symbol.iterator]
The iterable protocol allows JavaScript objects to define or customize their iteration behavior, such as what values are looped over in a for..of construct. when you define a constructor, give it this property...still a linked list
http://bit.ly/1MVVXxN
// give our type an iteration protocol const iterable = LinkedList()[Symbol.iterator] = () => // etcwe can make a linked list type that describes how it should be walked this makes our type "iterable"
possible infinity
http://bit.ly/1DzRyhL
What does it do?
It's about Iterables and Iterators
Garrett Dawson
@killtheliterate