Hacker News new | past | comments | ask | show | jobs | submit login
Underscore.js: Functional Programming for jQuery (documentcloud.github.com)
117 points by jashkenas on Oct 28, 2009 | hide | past | favorite | 35 comments



Looks like a very handy bunch of code, but I can't say I like the "_" part of it.

One of the neatest things about jQuery is the way all of the jQuery functionality lives in the "jQuery" namespace, with $ just set up as a convenient shortcut. If you don't want the $ in your global namespace (but still want it in your own code) you can do this:

    jQuery.noConflict();
    jQuery(function($) {
        // Code that uses the $ shortcut
    }
Feature request for underscore: something similar. If I'm already using the _ for something I'd like to still be able to use the underscore library, but bound to a different symbol.

    Underscore.noConflict();
    Underscore(function(_) {
        // My code that uses _
    }


Thanks for the suggestion, Simon -- It's done.

The Underscore page now has the download for version 0.1.1, with a noConflict function that returns the "_" variable to its previous value.

    var myUnderscore = _.noConflict();


That's awesome! Thank you very much.


Quick Summary: Underscore provides all the functional programming goodies that you would expect with Prototype.js or Ruby, but without extending any core javascript objects, so it can fit hand-in-hand with jQuery. In Javascript 1.6 compliant browsers, it delegates to the native versions of functions, so you can have your "map()" and run run it at native speed where available. The production version compresses to 4k, so there's not much overhead in adding it to a page.

Blog Announcement: http://www.documentcloud.org/blog/2009/10/28/underscore-dot-...

Git Repo: http://github.com/documentcloud/underscore

Test and Benchmark Suite: http://documentcloud.github.com/underscore/test/test.html


Awesome stuff but binding your library to _ (underscore) is unfortunate. That character is traditionally reserved for the gettext / i18n / gimme-a-string-in-human-language-X function.

    <h1>_('WELCOME_USER', user.firstName)</h1>
    <p>_('WELCOME_INTRO_TEXT')</p>
etc...


Or if you're into MooTools checkout http://github.com/ShiftSpace/functools for decorators, pre & postconditions, memoize, arrays and hashes as functions, currying any function parameter, function composition, arity dispatch, etc.

Check out http://github.com/ShiftSpace/promises for bringing functional sanity to AJAX requests as well support for lazy values:

  function example() {
    var remotes = ['a', 'b', 'c', 'd', 'e', 'f'];
    var result = "";
    while(remotev = remotes.shift()) {
      result = add(result, get(remotev)); // get is an async request! ;)
    }
    show(result); // -> console.logs "abcdef"
  }
No callbacks, your code looks like (and _is_) regular javascript even though this triggers 6 requests. AJAX callback code is the new spaghetti and this is an antidote.

And finally a http://github.com/ShiftSpace/set for a Set datastructure :)


Promises looks fun!

You might also be interested in http://osteele.com/sources/javascript/concurrent/ for another approach to this. [later, after reading more:] Actually, Promises looks much more complete and useful.


Thanks for this! I recall reading over your article, http://osteele.com/sources/javascript/functional/, back in the day and being completely baffled and intrigued. These days I've been hacking with Clojure and wanting to bring some of that FP sanity back to my daily work ;)


What does this have to do with jQuery?

edit: by that I mean it's useful without jQuery. It's already been added to Narwhal server-side JS package manager: http://github.com/kriskowal/underscore


Thanks to all your suggestions, Underscore 0.2.0 is out, with:

* "reduce" instead of "inject" (inject is now an alias).

* Other aliases added for Javascript 1.6+ standardized names (forEach, filter, every, some).

* "compose", and "lastIndexOf" added.


Check also: http://code.google.com/p/functional-javascript/

It's not tied to jQuery and approaches things in a particular way.


Underscore isn't tied to jQuery either. It just complements it nicely.

Oliver Steele's Functional is a great library too, and bits of Underscore were inspired by it. The main difference between the parts they have in common is that Functional.js extends the core Function prototype, while Underscore avoids altering any core objects.


Thanks! You can also feel free to mine http://github.com/osteele/collections-js, which is a version with just the collection functions (but still modifies built-in objects, so may not be to other people's taste), that I've been using in conjunction with jQuery. I haven't finished looking over your library, so I don't know if there's anything in there that would still be useful to you. There's a set of unit tests, which might be.

I eventually found that I was accumulating so many string functions that, in a version of this for OpenLaszlo here http://github.com/osteele/lzosutils, I broke them into a separate file.


Collections-js looks like a nice split -- I might end up nabbing a few more things out of there. Maybe some of the unit tests too, although, FYI, your Functional.js page has shown "Documentation Failed to Load" for me for the last few days (on Webkit).

One thing that it would be nice to agree on is the standard names for some of these things under Javascript. What should reduce be called? Inject? Foldl? I've followed the Prototype/Ruby naming conventions, but it might make sense to provide all of the commonly-used aliases -- especially when you're not extending core objects, and don't need to worry about cluttering them up. What do you think? Does the same function under 5 different names run as sweet?


Not inject. Inject was borrowed from smalltalk's #inject:into, but dropped the "into" and just ended up being unintuitive for many people. Ruby 1.8.7 and 1.9 now have #reduce, so I'd say that's the way to go. No reason to use foldl unless you're going to also have a foldr.


Version 0.2, which should be out this evening, will call it "reduce", and alias it to "inject". Thanks for the feedback.


I'd say just use the same function name. people will learn what is what as long as it sticks to some sort of consistent convention.


Thanks for the follow up. I checked the source shortly after my comment, but I forgot to came here and edit the "tied to jQuery" bit.


Thanks, jashkenas - this is fantastic.

I spend most of my day as a Ruby developer, but when I'm doing frontend work, the thing I miss most in Javascript is Ruby's incredibly simple, powerful list operations. While most of these have existed in some form for awhile now, I've never seen such a comprehensive implementation in just 5.2kb (minified) of very clean, simple, well-commented code.

This is probably going in every project I work on from here on out. Thanks for putting all of this together.


  >> _.first(array)
  >> "Convenience to return the first element of an array (identical to array[0])."
How is _.first(array) more convenient than array[0] ? :/


Sometimes you need a function. The alternative isn't array[0]; it's function (array) { return array[0]; }.


Ah thanks. Makes sense now.


When used in a functional context, these come to:

>> _.map(listOfLists, _.first)

vs.

>> _.map(listOfLists, function(array) { return array[0]; })

for example.


Yep -- that one is questionable. It's there to provide symmetry with "_.last", which is definitely more convenient than:

    array[array.length - 1];


It's more convenient to think about if your mind is in functional programming mode. Thought convenience is real convenience.


I know I've implemented a fair few of those functions on a more ad hoc basis in number of projects. So it's really nice to see someone putting it all together in a simple package. I'll certainly be having a closer look at the source code and trying it out on my current project.


This will go great with jQuery. I've missed some of these helpers when moving to jQuery from Prototype. I felt much better about not polluting globals after the switch, but this look like it's a nice half-way solution between Prototype's style and nothing at all.


0.3.0 is out, with a bunch of fine-tuning loop optimizations provided by Dmitry Baranovskiy, and CommonJS/Narwhal support, for those of you who use JavaScript on the server as well as the client.


Cool stuff. The only thing that keeps me from writing in a more functional style is the fact that no current browser implementations perform tail recursion optimization.


Why not extend jQuery with all these functions? Then we can write things like: jQuery.makeArray(document.getElementsByTagName("div")).inject(..)


Because those of us who are not going to use it shouldn't be burdened with it.

cat jquery.js functional.js > myfatquery.js and be happy.



nice, all this needs is a Class object and it'd be perfect (though i guess you could just add base.js to the mix).


Funny, that's precisely what we're doing at DocumentCloud -- Base.js for OOP / jQuery.js for DOM and Ajax / Underscore.js for functional...

Part of the idea is to make it easy to add functional utility methods to an existing project without making any other assumptions. You can use it with whatever OO scheme you've got cooked up, any Ajax library, and any DOM-manipulation framework.


What advantages would a "mix'n'match" approach like this have over something unified like Prototype (which, as far as I can tell, includes implementations of all the parts you are using)?

I have found Prototype much better for a "web-app" framework - I can build up complex controls and effects with the "OOP"/Inheritance framework, and I have access to all the functional tools I'm used to from Ruby.

JQuery, on the other hand, has always felt "lighter" - really great for a quick Carousel control on a marketing site, or a pop-up effect or something like that, but it quickly turns into spaghetti with anything more complex.

I'd be interested in other people's views on this split, especially people with experience using a whole toolset like yourself.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: