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

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

Post on 16-Apr-2017




1 download

Embed Size (px)


<p>To Infinity &amp; Beyond!</p> <p>Protocols &amp; Lazy Sequences in NodePart 1</p> <p>Bahul Neel Upadhyaya (@bahulneel)BraveNewTalent</p> <p>JavaScript</p> <p>Imperative</p> <p>Dynamic</p> <p>OOPPrototypes</p> <p>Constructors</p> <p>FunctionalFirst Class Functions</p> <p>Closures</p> <p>Brendan Eich</p> <p>Designed by Brendan Eich in 1994 for Mozilla.Originally called Mocha it was based on Self and SchemeBrings in features from both languagesAllows the developer to adopt any style or any combination of those styles.</p> <p>List Comprehension</p> <p>Loopsfor (init; while; step)</p> <p>Stack &amp; Queuepush/pop/shift/unshift</p> <p>IteratorsforEach, etc</p> <p>Map/Reduce</p> <p>No standard way of doing these</p> <p>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.</p> <p>Javascript has no standard way to deal with this.</p> <p>Collections</p> <p>How do I know if collection x works with my function?Extends Array?</p> <p>Implements Array?</p> <p>Impements some iterator?</p> <p>Do I need to monkey patch?</p> <p>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?=&gt; Complexity &amp; bloat</p> <p>Collections</p> <p>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?</p> <p>I think there is and all we need to do is borrow a few ideas from the Lisp guys.</p> <p>Lisp</p> <p>John McCarthy</p> <p>Structure and Interpretation of Computer Programs</p> <p>Designed byJohn McCarthy in 1958Made popular in AI circles by scheme written by Guy Lewis Steele Jr. and Gerald Jay Sussman.Sussman went on to write Structure &amp; 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</p> <p>Lists</p> <p>first</p> <p>rest</p> <p>first</p> <p>rest</p> <p>first</p> <p>rest</p> <p>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.</p> <p>It turns out that this representation gets us pretty far if we want a common way to deal with lists.</p> <p>Using Lists</p> <p>map = function(list, fn) {var head = first(list), tail = rest(list);if (!head) return [];result = map(tail, fn);return [fn(head)].append(result);</p> <p>};</p> <p>reduce = function(list, fn) {var head = first(list), tail = rest(list);if (!head) return null;result = reduce(tail, fn);return fn(head, result);</p> <p>};</p> <p>forEach = function(list, fn) {var head = first(list), tail = rest(list);if (!head) return null;fn(head);forEach(tail, fn);</p> <p>};</p> <p>filter = function(list, fn) {var head = first(list), tail = rest(list);if (!head) return [];result = map(tail, fn);if (fn(head)) return [head].append(result);else return result;</p> <p>};</p> <p>Sequences</p> <p>collection.first()Returns the first item</p> <p> a sequence of the the tail of the collection</p> <p>collection.cons(item)Returns a new collection with item at the head and the collection at the tail</p> <p>In Rich Hickey's Clojure dialect of LISP he added the concept of sequences which is any collection implementing the first, rest and cons methodsI've shown it on the slide in JS form.</p> <p>Now that we depend on an interface we can now also make an interresting observation.</p> <p>Lazy Sequences</p> <p>Only really care about the data in the list when we call first()</p> <p>rest() can be lazily evaluated </p> <p>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. </p> <p>Using Lazy Seqences</p> <p>map = function(coll, fn) {return lazySeq(function() {var head = coll.first(list), tail =;return lazySeq.cons(map(tail, fn), fn(head));</p> <p>});</p> <p>};</p> <p>The Interface Problem</p> <p>How do we check for the sequence interface?</p> <p>How do we apply the interface implementation if it's missing?</p> <p>What do we do if some object already has a first, rest or cons which are different from the ones we expect?</p> <p>Monkey Patching</p> <p>MyColl.protoype.first = function(coll) {} = function(coll) {}MyColl.protoype.cons = function(coll, item) {}</p> <p>The Adapter Pattern</p> <p>mySeq =mySeqAdapter(myColl);head = mySeq.first();tail =;newSeq = myColl.cons(item);</p> <p>Switching</p> <p>function first(coll) {if (coll === null)return null;</p> <p>else if (isArray(coll))return coll[0];</p> <p>else if ('function' === typeof coll.first)return coll.first();</p> <p>elsethrow new Error('not a sequence');</p> <p>}</p> <p>Is there some way to do this automatically?</p> <p>Yes: protocols</p> <p>Protocols</p> <p>var Sequence = protocol.define('Sequence',[ 'first', [ 'coll' ] ],[ 'rest', [ 'coll' ] ],[ 'cons', [ 'coll' ] ]</p> <p>);protocol.extend(SequenceMyCollection,[ 'first', function (coll) { return coll.head(); } ],[ 'rest', function (coll) { return coll.tail(); } ],[ 'cons', function (coll, item) { return coll.cons(item); }</p> <p>);</p> <p>Protocols vs. Interfaces</p> <p>Protocol</p> <p>Interface</p> <p>ImplementationProtocolsomeFunction</p> <p>Calls</p> <p>Calls</p> <p>InterfaceObjectsomeFunction</p> <p>Implements</p> <p>Calls</p> <p>Object</p> <p>Calls</p> <p>Think of the boxes as source filesMeaning the responsibility is decoulpled.</p> <p>Using Protocols</p> <p>var first = Sequence.first, rest =,cons = Sequence.cons;</p> <p>map = function(coll, fn) {return lazySeq(function() {var head = first(coll, list), tail = rest(coll, list);return cons(map(tail, fn), fn(head));</p> <p>});</p> <p>};</p> <p>What about Node?</p> <p>Sequences are synchronous</p> <p>Node is aynchronous</p> <p>Pull vs. Push</p> <p>Async in Node</p> <p>StreamsEvented</p> <p>Callbacks</p> <p>Buffered</p> <p>Composable</p> <p>PromisesSingle shot</p> <p>Callbacks</p> <p>Unbuffered</p> <p>Composable</p> <p>Async Map</p> <p>map = function(stream, fn) {var result = stream.Stream();result.writable = true;stream.on(data, function(data) {result.write(fn(data));</p> <p>});return result;</p> <p>};</p> <p>Async Sequences</p> <p>first(stream)A promise to the first item in the first buffer in the stream.</p> <p>rest(stream)A sequence of the rest of the items in the first buffer with the original stream at the tail.</p> <p>cons(stream, item)A sequence with a promise to the item at the head and the stream as the tail.</p> <p>Observations</p> <p>Lazy Sequences are like promises</p> <p>Syncronous values can be reresented as promises that resolve immediately</p> <p>With protocols we can conjoin sequences.</p> <p>Potential Uses</p> <p>Higher level abstractions</p> <p>Mix and match sync &amp; async</p> <p>Write once run anywhere</p> <p>Leverage new technologies e.g. WebCL</p> <p>Get cosy</p> <p>cosy.langProtocols</p> <p>Lazy Sequences</p> <p>Tail Recursion</p> <p> (v0) (v1+)</p> <p>To be continued...</p> <p>Part 2Async Sequences</p> <p>Tail Recursion</p> <p>Live Demos</p> <p>Questions</p> <p>CLIQUE PARA EDITAR O FORMATO DO TEXTO DO TTULO</p> <p>Clique para editar o formato do texto da estrutura de tpicos2. Nvel da estrutura de tpicos3. Nvel da estrutura de tpicos4. Nvel da estrutura de tpicos5. Nvel da estrutura de tpicos6. Nvel da estrutura de tpicos7. Nvel da estrutura de tpicos</p>