To Infinity & Beyond: Protocols & sequences in Node - Part 1

Uploaded on


More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads


Total Views
On Slideshare
From Embeds
Number of Embeds



Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

    No notes for slide
  • Designed by Brendan Eich in 1994 for Mozilla. Originally called Mocha it was based on Self and Scheme Brings in features from both languages Allows the developer to adopt any style or any combination of those styles.
  • One of the things we spend most of our time doing is manipulating lists of things (numbers, objects, etc). Currently, in the javascript world, we have a number of ways available to us. Classic for loop, push/pop, forEach, map, etc. Javascript has no standard way to deal with this.
  • Collections add a extra layer of complexity. Do they extend the Array prototype? If not do they implement the common array functions and/or patterns? If so which ones? Do I need to add these functions myself? When do I add them? Will they collide with someone elses functions? => Complexity & bloat
  • Is there a better way? Can we find a common set of methods that will let us write a universal function that should work with anyone's collection? I think there is and all we need to do is borrow a few ideas from the Lisp guys.
  • Designed by John McCarthy in 1958 Made popular in AI circles by scheme written by Guy Lewis Steele Jr. and Gerald Jay Sussman. Sussman went on to write Structure & Interpetation of Computer Programs with Harold Abelson (must read!) The name LISP was derrived from LISt Processing and had a very simple but powerful way to look at lists
  • In lisp a list is linked list that has a head and a tail. Where calling first on a list returns the data at the head of the list and calling rest returns a list of all the elements in the tail. It turns out that this representation gets us pretty far if we want a common way to deal with lists.
  • In Rich Hickey's Clojure dialect of LISP he added the concept of sequences which is any collection implementing the first, rest and cons methods I've shown it on the slide in JS form. Now that we depend on an interface we can now also make an interresting observation.
  • Until we actually ask for an element of the sequence we're not obliged to evaluate it. That means so long as we're able to evaluate the head at will we never need to bother with the rest until we need it. This means we can save on some unncecessary computation and also have potentially infinite sequences.
  • Is there some way to do this automatically? Yes: protocols
  • Think of the boxes as source files Meaning the responsibility is decoulpled.


  • 1. To Infinity & Beyond! Protocols & Lazy Sequences in Node Part 1Bahul Neel Upadhyaya (@bahulneel)BraveNewTalent
  • 2. JavaScript● Imperative● Dynamic● OOP – Prototypes – Constructors● Functional – First Class Functions Brendan Eich – Closures
  • 3. List Comprehension● Loops – for (init; while; step)● Stack & Queue – push/pop/shift/unshift● Iterators – forEach, etc● Map/Reduce No standard way of doing these
  • 4. Collections How do I know if collection x works with my function?● Extends Array?● Implements Array?● Impements some iterator?● Do I need to monkey patch?
  • 5. Collections
  • 6. LispJohn McCarthy Structure and Interpretation of Computer Programs
  • 7. Listsfirst rest first rest first rest
  • 8. Using Listsmap = function(list, fn) { reduce = function(list, fn) { var head = first(list), tail = rest(list); var head = first(list), tail = rest(list); if (!head) return []; if (!head) return null; result = map(tail, fn); result = reduce(tail, fn); return [fn(head)].append(result); return fn(head, result);}; };filter = function(list, fn) { forEach = function(list, fn) { var head = first(list), tail = rest(list); var head = first(list), tail = rest(list); if (!head) return []; if (!head) return null; result = map(tail, fn); fn(head); if (fn(head)) return [head].append(result); forEach(tail, fn); else return result; };};
  • 9. Sequences● collection.first() – Returns the first item● – Returns a sequence of the the tail of the collection● collection.cons(item) – Returns a new collection with item at the head and the collection at the tail
  • 10. Lazy Sequences● Only really care about the data in the list when we call first()● rest() can be lazily evaluated
  • 11. Using Lazy Seqencesmap = function(coll, fn) { return lazySeq(function() { var head = coll.first(list), tail =; return lazySeq.cons(map(tail, fn), fn(head)); });};
  • 12. The Interface Problem● How do we check for the sequence interface?● How do we apply the interface implementation if its missing?● What do we do if some object already has a first, rest or cons which are different from the ones we expect?
  • 13. Monkey PatchingMyColl.protoype.first = function(coll) {} = function(coll) {}MyColl.protoype.cons = function(coll, item) {}
  • 14. The Adapter PatternmySeq =mySeqAdapter(myColl);head = mySeq.first();tail =;newSeq = myColl.cons(item);
  • 15. Switchingfunction first(coll) { if (coll === null) return null; else if (isArray(coll)) return coll[0]; else if (function === typeof coll.first) return coll.first(); else throw new Error(not a sequence);}
  • 16. Protocolsvar Sequence = protocol.define( Sequence, [ first, [ coll ] ], [ rest, [ coll ] ], [ cons, [ coll ] ]);protocol.extend( Sequence MyCollection, [ first, function (coll) { return coll.head(); } ], [ rest, function (coll) { return coll.tail(); } ], [ cons, function (coll, item) { return coll.cons(item); });
  • 17. Protocols vs. Interfaces Protocol Interface Object Interface Calls Implements Implementation Calls Object Protocol Calls Calls someFunction someFunction
  • 18. Using Protocolsvar first = Sequence.first, rest =, cons = Sequence.cons;map = function(coll, fn) { return lazySeq(function() { var head = first(coll, list), tail = rest(coll, list); return cons(map(tail, fn), fn(head)); });};
  • 19. What about Node?● Sequences are synchronous● Node is aynchronous Pull vs. Push
  • 20. Async in Node Streams Promises● Evented ● Single shot● Callbacks ● Callbacks● Buffered ● Unbuffered● Composable ● Composable
  • 21. Async Mapmap = function(stream, fn) { var result = stream.Stream(); result.writable = true; stream.on(“data”, function(data) { result.write(fn(data)); }); return result;};
  • 22. Async Sequences● first(stream) – A promise to the first item in the first buffer in the stream.● rest(stream) – A sequence of the rest of the items in the first buffer with the original stream at the tail.● cons(stream, item) – A sequence with a promise to the item at the head and the stream as the tail.
  • 23. Observations● Lazy Sequences are like promises● Syncronous values can be reresented as promises that resolve immediately● With protocols we can conjoin sequences.
  • 24. Potential Uses● Higher level abstractions● Mix and match sync & async● Write once run anywhere● Leverage new technologies e.g. WebCL
  • 25. Get cosy● cosy.lang – Protocols – Lazy Sequences – Tail Recursion (v0) (v1+)
  • 26. To be continued...Part 2● Async Sequences● Tail Recursion● Live Demos
  • 27. Questions