Introducing a new data structure, streams, in Javascript
streamjs.org
streamjs.org
Want it to apply to data I/O? Stick your reader into the HEAD function, store the pointer to the next byte in TAIL, and implement functions for reading X number of bytes and converting them into their new types.
Yes, you can do math with it. You can also implement process/procedure into it, if you consider yourself popping lambdas off the stream, and which lambda is Next can be guided by choices in the current lambda.
Mostly, you can use this device to hold any collection of things and operate upon them in a streamy-syntax kind of way. For those who are familiar with jQuery, it is a bit of a reimplementation of a lot of jQuery's basic array-data handling with extra helpers like "range" tossed in and a stronger focus on the laziness. Where jQuery's prime target is work with the DOM, this stream package's target is the more theoretical version of some of jQuery's features, specifically those that iterate over the contained arrays. You could wrap jQuery in this and get an amazing hybrid.
Don't sell it short; it really is another useful and powerful way to approach data management. Check out how you construct your own stream with this package, and think beyond the basic numeric approaches.
Me too! http://jsfiddle.net/skilldrick/vT2mC/
I didn't get very far with it though - the combined issues of JavaScript's lack of tail-call optimisation and its verbose lambda syntax made building streams to painful.
This looks like an interesting approach, though you'll never get around JS's heavy lambdas (unless you use CoffeeScript or something).
http://weblog.raganwald.com/2007/02/haskell-ruby-and-infinit...
"A monad for enumerating sets: like the list monad, but impervious to infinite descent.
A depth-first search of a data structure can fail to give a full traversal if it has an infinitely deep path. Likewise, a breadth-first search of a data structure can fall short if it has an infinitely branching node. Omega addresses this problem by using a "diagonal" traversal that gracefully dissolves such data."
However, I do appreciate the usefulness of lazy evaluation. Still, I think this documentation could benefit by not making outlandish claims ("it will change the way you think") and just saying the author has implemented lazy evaluation in javascript (and maybe link to the wikipedia article on lazy evaluation and this stack overflow to explain why it's useful: http://stackoverflow.com/questions/2151226/what-are-the-adva...)
As far as changing the way you think, it certainly came as a shock for me when I encountered it years ago in SICP and I'm still enjoying the benefits of lazy sequences in Clojure and ClojureScript.
http://danielnouri.org/docs/SuperColliderHelp/Streams-Patter...
http://danielnouri.org/docs/SuperColliderHelp/Streams-Patter...
SuperCollider is a fun language to study. You end up with compositional structures like the following (notice 'inf', which indicates an infinite loop)
Pbind(
\octave, 4,
\degree, PstepNadd(
Pseq([1, 2, 3]),
Pseq([0, -2, [1, 3], -5]),
Pshuf([1, 0, 3, 0], 2)
),
\dur, PstepNadd(
Pseq([1, 0, 0, 1], 2),
Pshuf([1, 1, 2, 1], 2)
).loop * (1/8),
\legato, Pn(Pshuf([0.2, 0.2, 0.2, 0.5, 0.5, 1.6, 1.4], 4), inf),
\scale, #[0, 1, 3, 4, 5, 7, 8]
).play;Several commenters have compared these streams to Python generators. Yes, the two are similar. But there are are a couple of important things Python generators have, that these don't.
(1) Python generators, along with lists, tuples, files, net connections, etc., all support the standard iterator protocol. This means that, for many purposes, you don't need to know or care which kind of data source you are reading from. The itertools functions can be applied to all of them, etc. In contrast, these JS streams are dealt with via specialized member functions. You can't just take array-based code, pop in a stream, and expect it to work.
(2) The iterator returned by a Python generator is a wrapper around a single running computation, which can spit out multiple values. Retrieving a new value is not, at heart, a function call, but rather the continued execution of a coroutine that has already started. This has many advantages; for example, we can write a Python generator that produces an infinite dataset, using a simple loop. I don't think that works here.
Now, as for (1) above, I think this is something we could do in JS. It would just be a matter of everyone agreeing to support a particular interface.
OTOH, it seems to me that JS does not have the functionality to support (2). Python does. So do Ruby, Haskell, and Go -- each in its own way. But JS does not, AFAIK. Am I wrong? And should we be building support for (2) into JS and other languages?
-----
BTW, I find these streams to be pedagogically interesting. Unlike, say, Haskell lists, the thunks in the data structure can be stored explicitly. It's a nice exercise in the actual implementation of a lazy sequence.
As for (1), I'd be happy to hear a proposal. If you'd like to work on this, feel free to submit an issue or even a pull request on github — suggestions like this are very welcome. Thanks! :)
Generators like in Python allow for nice AI scripting with explicite time-sharing - each AI actor is generator function, that in main loop has yield statement.
That way it all works in one thread, and ai code can be written straightforward, like each actor had it's own thread.
In any case, are you sure that JS generators should be considered a "future" thing? I just did a bit of research, and it sounds like you are talking about this proposal:
http://wiki.ecmascript.org/doku.php?id=proposals:iterators_a...
According to the following page, much/all of that proposal was included in JS 1.7, which was implemented in Firefox 2.0.
https://developer.mozilla.org/en/New_in_javascript_1.7
And FF 2.0 was released in October 2006. A quick check shows that my FF installation (3.6.22) supports generator functions, and the "yield" keyword, just fine, as long as I say
<script type="text/javascript;version=1.7">
Regardless, I'm a little out of my depth here.Lazy lists are pretty neat, and if you make them a convenient part of a programming language and its software ecosystem, they can be useful. This JavaScript version, though, looks more like a clever toy at the moment. Maybe that will change eventually.
I can explain classes in 10 minutes and have the same effect. Assuming, of course, you've only coded FORTRAN.
In fact, you picked out the part that I mostly found mildly offensive. He's treating his readers like "less smart than him".
Now here's where I really blow your brain: a Sequence is some stuff, in a particular order. What about the keyUp event? If I were to listen to that event, it might give me ['T','e','s','t'].
That too, is some stuff, in order. This means, that Events are Sequences. Which means, that all the cool stuff that can be done to Sequences, can be done to an Event too, which is exactly what the Reactive Extensions for JS from Microsoft does (disclaimer: I'm writing a book on this).
Haskell of course has this too, it's called the Continuation Monad.
That's hilarious! I'm seriously fighting my social-urge to upvote...
Generators a great way to do processing on a lot of data, in a very list-y way, without having to read all the data into memory first. It allows you to start seeing results straight away.
For example, you can define powers of two as:
var powersOf2 = new Stream(1, function() {
return powersOf2.scale(2);
});
(untested) which means that the sequence of powers of 2 begins with 1 follows with the the same sequence scaled by 2!The implementation in the linked article however seems to be flawed, since it doesn't store the node's tail after computing it. This means that to get each element of the sequence you have to compute all the elements before it (even though you already did in order to get to that element).
This works not only for numbers but for other infinite sequences such as expressions in a language. I recommend the book Higher Order Perl if you're interested in this.
var Lazy = require('lazy');
Lazy.range('1..') // infinite list of integers (see readme)
.filter(function (n) {
return n % 5 == 0
})
.take(5)
.join(function (result) {
console.log(result)
})
;
Output: [ 5, 10, 15, 20, 25 ]Here's another example you might want to include, if you're going for the whole Haskell angle. Not as elegant, but illustrates the concept:
var fibs = new Stream(1, function() {
return new Stream(1, function() {
return fibs.zip(function(a, b) { return a + b; }, fibs.tail());
});
});
fibs.take(10).print(); Enumerable
.Range(0, 1000000000000)
.Take(10)
.ToArray()
Output: 0,1,2,3,4,5,6,7,8,9Streams seems to be a proper subset of the things you can do with linqjs. Sometimes MSFT can do cool things too, it just doesn't always get picked up :)
Enumerable
.Generate((function() {
var i = 0;
return function() {
return i++;
};
})())
.Take(10)
.ToArray()
Output: 0,1,2,3,4,5,6,7,8,9I can think of a few fun uses (such using them as creative replacements for looping constructs), but I'm struggling to find a problem that is most easily solvable with these.
However, right at the bottom the etymology of the name is made clear:
> The name 'stream' is used in Scheme, a LISP dialect that supports these features.
So the name is from the FP community. Given the cultural issues, a name change seems unlikely...
I can't think of anywhere to use this in the context of building applications and I certainly don't feel like it "completely changed the way I think about programming" at all.
Edit: So it looks like you can put more than just numbers into the streams -- still what does this do that Underscore.js doesn't?
I've been playing around with this in my current project, yet I'm not convinced this is always such a great model. It requires a lot of mental work to remember what types you are operating on at each state in the pipeline. That being said, I'm a big fan of LINQ, and I'd like to see this model applied more frequently to DB queries.
ClojureScript of course takes it even further.
1. http://brendaneich.com/2011/08/my-txjs-talk-twitter-remix/
To start with, let's create a word called "count" which produces a generator which will count from 1 to infinity:
: count ( -- 'gen )
noname create 0 , latestxt
does> ( -- value? flag )
dup @ 1+ dup >r swap ! r> true
;
Then we build another generator which slices the first N elements of another generator, so that we can do things without going into an infinite loop: : slice ( 'gen count -- 'gen )
noname create , , latestxt
does> ( -- value? flag )
dup @ 0= if drop false exit then
dup dup @ 1- swap !
cell+ @ execute
;
Finally, let's implement "map", so that we can apply a word to every element produced by the generator: : map ( 'gen 'func -- )
2>r
begin
i' execute
if i execute
else 2rdrop exit
then
again
;
Now the Forth stack gives us a nice, fluent way of chaining these building blocks together. Let's start with that infinite counter, slice off the first 10 elements and print them out by mapping them to the number printing word ".": count 10 slice ' . map
And we get: 1 2 3 4 5 6 7 8 9 10
Spiffy, hunh?Not terribly useful imho and certainly doesn't live up to the hyped intro.
For me, I thought I had the answer after maybe 30 seconds of reading it, but decided to "check my work" by walking through it again, and only then, a moment later, did I realize what it was actually doing with that recursive call.
I'm just curious about their "most programmers without a functional background" remark. So: did ya get it?
Still, I'm not sure that a functional background would have helped, or maybe my background is more functional than I think. I know some Python, and I read a few articles about functional programming a couple of years ago, but I don't think I could give you a clear definition of what functional programming is.
- Streams are an alternative to generators (yield)
- Streams can be used to traverse recursive structures
- Streams implementation only require lambda functions with proper closure
- Node.js continuation passing style is a form of Stream
I've implemented streams using continuations. You can also see a bunch of related functions such as: print, map, filter, reduce, range, enumerate, comprehension, traverse, empty, cons, zip, xrange, generator2stream.
http://blog.vjeux.com/2011/javascript/stream-lazy-iteration-...
Tell me if you have any remark :)
In ClojureScript, streams or lazy lists are an integral language feature. Standalone javascript libraries can not provide a functional framework of the same calibre. You would naturally end up wanting to, e.g., run an underscore.js chain on a stream and that would require more work.
However, I use Javascript functions as base instead of a Stream constructor.
Based on your suggestion, I've added a few clarifying comments, especially in the 'Tributes' section to show that this isn't my own brand new idea but is based on other people's work; maybe this will help. Thanks :)