dtao / lazy.js

Like Underscore, but lazier
http://danieltao.com/lazy.js/
MIT License
6.01k stars 267 forks source link

Integration into the default types #104

Open koraa opened 10 years ago

koraa commented 10 years ago

Hi

I like the functional, lazy style of thinking very much, so I was quite excited when I first heard of Lazy.js!

There is one drawback though which is not really a fault of Lazy.js but rather that JS is not designed as lazy. Frequently there is a rather simple task, like I want to uppercase a string:

(Sorry for the Coffescript; I can convert it to JS if you wish)

  upp = (s) ->
    Lazy(s).toUpperCase().toString()

My code becomes more type conversion than anything else.

This would be all fine if I could just ignore the normal functions and always expect lazy data types as arguments/return value. I can not however because most libraries (and bits of existing code in my projects) expect that the native non-lazy types are used.

Here is another example; let's say I want to process an array, cast to a string and process that string and return it:

  specialString = (v) ->
    s__ = Lazy v
      .map (x) -> x+1
      .toString()

    Lazy s__
      .substring 1, 8
      .toString()

My solution in these simple cases is normally to fall back to the native functions or underscore; I don't think this is a very good solution and I think it would be much nicer if I just could write something like this and have support for both – code that is not written explicitly for lazy.js and code that is.

  upp = (s) -> s.toUpperCase()

  specialString = (v) ->
    v.map (x) -> x+1
     .toString()
     .substring 1,8

I realize that there are much better ways to implement the snippets above, but I hope they they serve to illustrate my observations.

I thought about this problem but I could not really find a solution, so I thought I'd put this problem out here and ask if we maybe can come up with a way to better integrate those two worlds.

Despite of this I've now used lazy.js for a while productively and it is really cool! Thanks for the hard work guys!

dtao commented 10 years ago

I think I understand your point, and if I understand you correctly then I totally agree. Easily the greatest source of "friction" in using Lazy is that sequences are not inherently interoperable with other libraries that always expect arrays.

I'm not sure I have a good solution to this. To some extent, especially to maintain Lazy's solid performance, I don't think there is a solution. At the same time, for devs who value a low-friction solution above breakneck performance, maybe I can figure out some optional feature to make this more seamless. (For example, I'm thinking about using an ES6 Proxy object to wrap around a sequence and act like an array.)

I'll update this issue if/when I decide either how to do something like this, or that I'm not doing it.

koraa commented 7 years ago

Hi!

So I am again evaluating lazy.js. The good news is I can now basically say "my function takes and returns es6 iterators" and that is working pretty well. Lazy JS is an es6 iterator, so returning that works fine. The problem is: I still need the cast to lazy JS.

I wrote my own lib based on es6 iterators – just to get a feel for the problem:

  var {isPlainObject} = lodash;

  function iter(seq){
    if (isPlainObject(seq)) {
      function* pairs() {
        for (var k in seq) {
          yield [k, seq[k]];
        }
      };
      return pairs();
    } else {
      return seq[Symbol.iterator]();
    }
  };

  function* map(seq, f) {
    for (var k of iter(seq)) {
      yield f(k);
    }
  }

  function each(seq, f) {
    for (var nul of map(seq, f));
  }

I usually import that with something like: var {iter, map, each} = itertools; I also used my own version of the Lazy() function – I called it iter() (it's a no-op except for objects).

But I call that implicitly map() and each(), so when I use those functions I can avoid the extra call to iter(). Would it be possible to do something similar in lazy.js?

I mean, provide functions like Lazy.map(iter, function)?

dtao commented 7 years ago

If I understand what you're asking, you want it to be possible to call Lazy(iterable) and get a Lazy sequence. Is that right?

koraa commented 7 years ago

that too, but I was going to search if there was an issue for that already :)

I want Lazy.map(iterable, function) or Lazy.map(function, iterable) and I want Lazy.filter(function, iterable) and Lazy.foldl(function, initial, iterable) and …

These could probably be generated by meta programming. No need to manually write them.

koraa commented 7 years ago

There is: https://github.com/dtao/lazy.js/issues/66 :)