Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.javascript > #29622 > unrolled thread

library for fast map/filter/reduce

Started byglathoud <glathoud@yahoo.fr>
First post2016-02-16 22:24 -0800
Last post2016-02-23 22:11 -0800
Articles 9 — 4 participants

Back to article view | Back to comp.lang.javascript


Contents

  library for fast map/filter/reduce glathoud <glathoud@yahoo.fr> - 2016-02-16 22:24 -0800
    Re: library for fast map/filter/reduce Scott Sauyet <scott.sauyet@gmail.com> - 2016-02-18 20:01 -0800
      Re: library for fast map/filter/reduce glathoud <glathoud@yahoo.fr> - 2016-02-19 02:10 -0800
        Re: library for fast map/filter/reduce Scott Sauyet <scott.sauyet@gmail.com> - 2016-02-19 08:44 -0800
          Re: library for fast map/filter/reduce glathoud <glathoud@yahoo.fr> - 2016-02-20 04:03 -0800
            Re: library for fast map/filter/reduce Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-02-20 13:59 +0000
              Re: library for fast map/filter/reduce glathoud <glathoud@yahoo.fr> - 2016-02-23 21:53 -0800
    Re: library for fast map/filter/reduce "Michael Haufe (TNO)" <tno@thenewobjective.com> - 2016-02-23 19:22 -0800
      Re: library for fast map/filter/reduce glathoud <glathoud@yahoo.fr> - 2016-02-23 22:11 -0800

#29622 — library for fast map/filter/reduce

Fromglathoud <glathoud@yahoo.fr>
Date2016-02-16 22:24 -0800
Subjectlibrary for fast map/filter/reduce
Message-ID<9f70cd71-da91-478c-ac97-392fcb7c7a63@googlegroups.com>
Hello,

I'd like to share a library that permits to write map/filter/reduce code that runs much faster than the corresponding native Array methods. It works by generating fast JS code automatically, with a single for loop whenever possible, and also includes logical functions and object/array conversions functions.

http://glat.info/transfun/

Best regards,
Guillaume

[toc] | [next] | [standalone]


#29635

FromScott Sauyet <scott.sauyet@gmail.com>
Date2016-02-18 20:01 -0800
Message-ID<8e0eb84d-66c8-41da-847b-759f634bc4cd@googlegroups.com>
In reply to#29622
glathoud wrote:
> I'd like to share a library that permits to write map/filter/reduce code 
> that runs much faster than the corresponding native Array methods. It 
> works by generating fast JS code automatically, with a single for loop
> whenever possible, and also includes logical functions and object/array 
> conversions functions.
> 
> http://glat.info/transfun/

Please, when posting to USENET, give more information than a sound-bite
and a link.  The link is fine, but it should be backed up by further 
discussion, code, questions, debate, flame-wars, pedantry, ... in short,
by *content*.  You certainly do not need to replicate all the content to
which your link makes reference, but there should be enough that people
here can discuss it without chasing down the link, and only visit that
page if it still seems interesting to them.

-----

Now, as to your library, congratulations!  It's very interesting.  It
seems to combine aspects of two ideas that I haven't seen put together
before.  First, it uses a syntax very similar to Oliver Steele's
Functional JavaScript [1], the first fairly comprehensive library for
doing functional programming in Javascript.  The similarity here is
in the string-based lambda expressions, such as `'.p'` for the 
equivalent of `obj => obj.p` or `function(obj) {return obj.p;}`, and
`'+'` for `(a, b) => a + b`.

Second, you implement the relatively new notion of *transducers*
[2], which, at their core, replace a sequence of iterations of
transformations with an iteration of a sequence of transformations. 
This prevents the creation of intermediate containers and can gain
some serious efficiency.

There are two reasons that this API does not appeal to me.  First,
although I appreciate the _tour de force_ that was Oliver Steele's
original library, I don't at all care for the string lambdas.  They
feel entirely wrong, as though all the lessons we've learned over 
the years about problems with `eval` are to be discarded.  Second,
I absolutely prefer to work by building composing more sophisticated
functions out of simpler ones.  Chaining methods together holds
absolutely no appeal.

So this example from transfun:

    var appfun = tfun.map( '.p' ).filter( '!=null' ).reduce( '+' );

I would write using Ramda as

    const appfun = pipe(map(prop('p')), filter(complement(isNil)), sum);

The main difference is that I can pass ANY function through a pipeline
like this.  It does not have to be a fixed method of some object or 
set of objects.  So for instance, if I wanted to reuse the ability to
filter out the null values, I would simply name that function:

    const removeNulls = filter(complement(isNil));
    const appfun = pipe(map(prop('p')), removeNulls, sum);

I don't see a way to do this with a chained set of method calls.  But
I have only looked at your documentation and haven't dug into the code
so I may be missing something interesting.

But regardless of whether it's something that appeals to me personally,
kudos on putting this together.  There's definitely some interesting
ideas here!

  [1]: The original documentation page seems to be offline, but the code
       is on GitHub at <https://github.com/osteele/functional-javascript>
       and an initial announcement is at 
       <https://github.com/osteele/functional-javascript>

  [2]: First discussed in the Clojure community, e.g.: 
       <http://blog.cognitect.com/blog/2014/8/6/transducers-are-coming>.
       There are several well-known Javascript versions, including 
       <https://github.com/jlongster/transducers.js> and
       <https://github.com/cognitect-labs/transducers-js>.  They're also
       partially folded into some more general-purpose libraries like
       Ramda: <http://ramdajs.com> (disclosure: I'm one of the authors.)


  -- Scott

[toc] | [prev] | [next] | [standalone]


#29636

Fromglathoud <glathoud@yahoo.fr>
Date2016-02-19 02:10 -0800
Message-ID<286742ed-e3f3-466f-9b4d-b9deb5bb4442@googlegroups.com>
In reply to#29635
Scott, thank you very much for taking the time to write such a detailed response. Below is an attempt to address several points you made.

.

Concerning O'Steele functional JavaScript: if I remember correctly, he was the one behind most of dojox.lang.functional back then. My article pointed to this already:

http://glat.info/transfun/#interlude-personal-story

I should probably add the link you provided.

.

Concerning the string lambdas: transfun.js permits both string lambdas and usual functions:

http://glat.info/transfun/#with-functions

I am personally happy with both:

http://glat.info/transfun/#personal-opinion

It is probably a matter of context.

.

Concerning transducers, the term may be relatively new, but I am not sure whether the underlying mechanisms are that new. See for example this discussion:

http://forum.dlang.org/post/mailman.166.1409468422.5783.digitalmars-d@puremagic.com

.

Thanks for mentionning Ramda. My impression after a brief glance: that's a lot of functionalities! Certainly something to look at.

.

Side note: transfun.js offers a few selected transformations (especially the ones I often need), but anyone can write his own:

http://glat.info/transfun/#defining

.

Thanks again and best regards,
Guillaume

[toc] | [prev] | [next] | [standalone]


#29638

FromScott Sauyet <scott.sauyet@gmail.com>
Date2016-02-19 08:44 -0800
Message-ID<f9863dd4-3361-4fc6-ab37-808b4222887a@googlegroups.com>
In reply to#29636
glathoud wrote:
> Scott, thank you very much for taking the time to write such a detailed 
> response. 

I was glad to.  It's a very interesting library.  And please do take the
criticism in the positive spirit in which it was intended.  This is the
most interesting new library I've seen since Trine [1].  That it doesn't
appeal directly to my personal sensibilities doesn't mean I think it
won't appeal to others'.

> Concerning O'Steele functional JavaScript: if I remember correctly, he was 
> the one behind most of dojox.lang.functional back then. 

Oh, was he?  I always edged my way around Dojo, using it when I was on
projects that already had it, but never introducing it.  It always seemed
overblown, a designed-by-committee monstrosity.


> Concerning the string lambdas: transfun.js permits both string lambdas and
> usual functions:
> 
> http://glat.info/transfun/#with-functions
> 
> I am personally happy with both:
> 
> http://glat.info/transfun/#personal-opinion
> 
> It is probably a matter of context.


Perhaps.  I did see that both were available.  To me, though, the string
lambdas are uncomfortable.  They make me wonder about the overall design.
If I were to use the library, though, I do understand that I could simply
choose not to use them.

These days, ES6 fat arrow syntax helps reduce the verbose nature of lambdas
in JS anyway, so some of the reason for these may not be so important.  I
may be missing some of your motivation, though.


> Concerning transducers, the term may be relatively new, but I am not sure 
> whether the underlying mechanisms are that new. See for example this 
> discussion:
> 
> http://forum.dlang.org/post/mailman.166.1409468422.5783.digitalmars-d@puremagic.com

Interesting.  I've not worked with D, only read a few articles about it.

I've always thought of it as a mostly imperative language, and didn't
even realize that it had functional APIs included.  I can't tell from
their documentation if the claims from that post are justified or not.
It would be interesting to know.

Clearly this cannot be the first time someone has wanted to address this
problem.  Transducers is the first time I saw a generic approach at a
solution, but it would not be surprising to find that others had already
solved it.


> Thanks for mentionning Ramda. My impression after a brief glance: that's 
> a lot of functionalities! Certainly something to look at.

If nothing else, it's been a fun project.



> Side note: transfun.js offers a few selected transformations (especially 
> the ones I often need), but anyone can write his own:
> 
> http://glat.info/transfun/#defining

That does improve the situation, but still doesn't give the expressiveness
that I like in pipelines.  It doesn't allow me to use functions on the
fly, partially applying functions, using lambdas, etc.

Again, though, I do find this very interesting, and I look forward to
having the time to dig more deeply into the implementation.

Kudos!

  [1]: Trine came on very strong, but peaked quickly and didn't seem to 
       last.  I wasn't really interested in using it either, but it also 
       presented an interesting alternative approach that I hadn't seen  
       elsewhere:  <https://github.com/jussi-kalliokoski/trine>

  -- Scott

[toc] | [prev] | [next] | [standalone]


#29640

Fromglathoud <glathoud@yahoo.fr>
Date2016-02-20 04:03 -0800
Message-ID<56e25c80-9c8e-4113-a6ad-07d7c321b5db@googlegroups.com>
In reply to#29638
On Friday, February 19, 2016 at 5:44:31 PM UTC+1, Scott Sauyet wrote:

> [...] please do take the
criticism in the positive spirit in which it was intended. [...]

No worries ! That's the way I understood your comments. And thanks for the compliments.

> [...] I always edged my way around Dojo, using it when I was on
> projects that already had it, but never introducing it.  It always seemed
> overblown, a designed-by-committee monstrosity.

Yes... I once removed dojo from a production codebase, thus putting an end to recurring "daytime nightmares". I found comp.lang.javascript particularly useful in that context.

> 
> 
> > Concerning the string lambdas: transfun.js permits both string lambdas and
> > usual functions [...] It is probably a matter of context.
> 
> Perhaps.  I did see that both were available.  To me, though, the string
> lambdas are uncomfortable.  They make me wonder about the overall design.
> If I were to use the library, though, I do understand that I could simply
> choose not to use them.
> 
> These days, ES6 fat arrow syntax helps reduce the verbose nature of lambdas
> in JS anyway, so some of the reason for these may not be so important.  I
> may be missing some of your motivation, though.

One possible motivation is the extra speed - although the speed gain is not as important as the speed gain obtained from eliminating intermediate arrays. 

Another possible motivation is to generate flat code without resorting to function decompilation. 

A third motivation is that I just like to generate code :) More seriously it is nice to be able to look at one piece of generated code, hopefully still somewhat readable.

Side note about lambdas: it would be nice to be able to write some without variables at all. OCaml has an interesting notation where one can wrap an operator + into a function (+) 

> 
> [...]
> 
> Interesting.  I've not worked with D, only read a few articles about it.
> 
> I've always thought of it as a mostly imperative language, and didn't
> even realize that it had functional APIs included.  I can't tell from
> their documentation if the claims from that post are justified or not.
> It would be interesting to know.

I had the same reaction and asked. The answer is yes:

http://forum.dlang.org/thread/pbcwqhfgjghxezpsiblo@forum.dlang.org

>
> [...]
>
> > Side note: transfun.js offers a few selected transformations (especially 
> > the ones I often need), but anyone can write his own:
> > 
> > http://glat.info/transfun/#defining
> 
> That does improve the situation, but still doesn't give the expressiveness
> that I like in pipelines.  It doesn't allow me to use functions on the
> fly, partially applying functions, using lambdas, etc.

Ok. At this point of time I can only say that transfun's transformations support automatic currying - not documented, sorry. I have to look at the other topics you mentionned.

Thanks for mentionning Trine.

Best regards,
Guillaume

[toc] | [prev] | [next] | [standalone]


#29642

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2016-02-20 13:59 +0000
Message-ID<87lh6f32k1.fsf@bsb.me.uk>
In reply to#29640
glathoud <glathoud@yahoo.fr> writes:
<snip>
> Side note about lambdas: it would be nice to be able to write some
> without variables at all. OCaml has an interesting notation where one
> can wrap an operator + into a function (+)

Haskell has that too, and it came (at least in the case of Haskell),
from Miranda designed by David Turner.  He may have got it from a 1973
dissertation by Wile and (like so many such things) it probably has even
older roots in mathematical logic.

Haskell (and Miranda) can go both ways.  (+) is a function make from an
operator, but given a function like mod, you can make an operator from
it using `...`:

  95 `mod` 10

is 5.  And you can combine the two.  Since operators permit sections
using either operand it can be handy to write (`mod` 10) for the "modulo
10" function.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#29675

Fromglathoud <glathoud@yahoo.fr>
Date2016-02-23 21:53 -0800
Message-ID<055e2127-2b3a-4d16-a969-d4e09a9b070c@googlegroups.com>
In reply to#29642
On Saturday, February 20, 2016 at 2:59:52 PM UTC+1, Ben Bacarisse wrote:
> glathoud <glathoud@yahoo.fr> writes:
> <snip>
> > Side note about lambdas: it would be nice to be able to write some
> > without variables at all. OCaml has an interesting notation where one
> > can wrap an operator + into a function (+)
> 
> Haskell has that too, and it came (at least in the case of Haskell),
> from Miranda designed by David Turner.  He may have got it from a 1973
> dissertation by Wile and (like so many such things) it probably has even
> older roots in mathematical logic.
> 
> Haskell (and Miranda) can go both ways.  (+) is a function make from an
> operator, but given a function like mod, you can make an operator from
> it using `...`:
> 
>   95 `mod` 10
> 
> is 5.  And you can combine the two.  Since operators permit sections
> using either operand it can be handy to write (`mod` 10) for the "modulo
> 10" function.
> 
> <snip>
> -- 
> Ben.

Thanks for giving the big picture about this topic. I did not know all that!

Best regards,
Guillaume

[toc] | [prev] | [next] | [standalone]


#29674

From"Michael Haufe (TNO)" <tno@thenewobjective.com>
Date2016-02-23 19:22 -0800
Message-ID<ea3fa9d4-d683-4081-a605-14d084d2a430@googlegroups.com>
In reply to#29622
On Wednesday, February 17, 2016 at 12:24:58 AM UTC-6, glathoud wrote:
> Hello,
> 
> I'd like to share a library that permits to write map/filter/reduce code that runs much faster than the corresponding native Array methods. 

This of course depends on the implementation. A sufficiently intelligent one can accomplish comparable speed. For example, take a look at the newer implementations of Firefox [1]

> It works by generating fast JS code automatically, with a single for loop whenever possible, and also includes logical functions and object/array conversions functions.
> 
> http://glat.info/transfun/

It seems like you've rediscovered something similar to the fold-fusion laws [2][3]. It's good to see someone play with this in JavaScript again, though I'm not a fan of your syntactic approach of using string literals... When I played with similar in the past (LINQ implementation on top of iterators/generators), I was using the following to represent the expressions:

function sum(a,b){ return a + b; }

var query = xs.select('p').where({ne:null}).reduce(sum)
...
query() //call the query

If I was to revisit this again, I think I would look at the recent efforts of Asen Bozhilov on ES-Iter [4] combined with a re-read of a few of the fold-fusion papers.

Overall though, I have a positive opinion of your efforts.

[1] <http://jsperf.com/arr-map-filter-reduce/5>
[2] <http://www.cs.nott.ac.uk/~pszgmh/fold.pdf>
[3] <https://www.cs.ox.ac.uk/ralf.hinze/publications/IFL10.pdf>
[4] <https://github.com/abozhilov/ES-Iter>

[toc] | [prev] | [next] | [standalone]


#29676

Fromglathoud <glathoud@yahoo.fr>
Date2016-02-23 22:11 -0800
Message-ID<7c859d10-8d98-4fe5-a26c-d370344c77bf@googlegroups.com>
In reply to#29674
On Wednesday, February 24, 2016 at 4:22:55 AM UTC+1, Michael Haufe (TNO) wrote:
> On Wednesday, February 17, 2016 at 12:24:58 AM UTC-6, glathoud wrote:
> > Hello,
> > 
> > I'd like to share a library that permits to write map/filter/reduce code that runs much faster than the corresponding native Array methods. 
> 
> This of course depends on the implementation. A sufficiently intelligent one can accomplish comparable speed. For example, take a look at the newer implementations of Firefox [1]

Well I'd be cautious with the interpretation of that result. One can also say that the optimization of the single loop is deficient in Firefox, especially when one compares with other browsers. For example, Chrome is running it more than one order of magnitude faster! (and if I recall correctly, Internet Explorer, too).

In practice, if transfun leads to a comparable or slightly higher speed in some browsers, and a much higher speed in other browsers, that's good enough for me - especially in the mobile context for performance-critical parts of an application. In my experience, without such tools like transfun, profiling leads invariably to write loops by hand, which is a bit boring after a while.

> 
> > It works by generating fast JS code automatically, with a single for loop whenever possible, and also includes logical functions and object/array conversions functions.
> > 
> > http://glat.info/transfun/
> 
> It seems like you've rediscovered something similar to the fold-fusion laws [2][3].

Yes. The second paragraph of the transfun article already says:

> transfun.js automatically merges consecutive loops into one loop, then 
> generates fast code for that loop (similar to stream fusion in → Haskell).


> It's good to see someone play with this in JavaScript again, though I'm not a fan of your syntactic approach of using string literals... When I played with similar in the past (LINQ implementation on top of iterators/generators), I was using the following to represent the expressions:
> 
> function sum(a,b){ return a + b; }
> 
> var query = xs.select('p').where({ne:null}).reduce(sum)
> ...
> query() //call the query
> 
> If I was to revisit this again, I think I would look at the recent efforts of Asen Bozhilov on ES-Iter [4] combined with a re-read of a few of the fold-fusion papers.
> 
> Overall though, I have a positive opinion of your efforts.
> 
> [1] <http://jsperf.com/arr-map-filter-reduce/5>
> [2] <http://www.cs.nott.ac.uk/~pszgmh/fold.pdf>
> [3] <https://www.cs.ox.ac.uk/ralf.hinze/publications/IFL10.pdf>
> [4] <https://github.com/abozhilov/ES-Iter>

Thanks for [2][3][4]. Very interesting. [4] looks pretty good to me. I am not sure how complicate it is to debug such code in practice, though. This is also one of the possible reasons to "embrace the ugliness" of generated code - although of course within reasonable limits.

Best regards,
Guillaume


[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.javascript


csiph-web