Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Practical functional programming in JavaScript (extralogical.net)
62 points by walrus on July 5, 2012 | hide | past | favorite | 38 comments


> Currying is the process of converting a function of arity n into a nested set of functions with arity 1, i.e. making it partially applicable

If a reader knows what the words in the description mean, then presumably they know what currying is...

Wouldn't it be better to have documentation that illuminated, rather than showed off your grasp of programming terms?


Of course it would. Patches which improved the documentation would be very welcome.


Also it seems a bit weird to have 'any' and 'filter' without having 'first'.


Not really -- "any" and "filter" are total functions, but "first" of an empty list is undefined.

On the other hand, that also applies to foldl1 and {max,min}imum{,By}, so it's probably not the reason.

The reason is probably that in JavaScript you can just say a[0] to get the first element element of a.


It seemed unnecessary, yes, although I suppose there are possibly some circumstances where one might want a `first` function—for example if one were folding over a list of lists.


There are an infinite number of circumstances where one would want 'first'.

In Lisp terms[1], 'first' is 'car', which, in conjunction with 'cdr'. With that combination recursive logic is regularly implemented.

[1] Some Lisps already include this alias.


In most cases the "a[0]" approach works, however. The set of circumstances where you want "first" as a proper function is limited to those cases where you need to pass it into some higher order function (which is still infinite, granted, but it's not 'every time we'd want "car"').


As both a functional and a javascript programmer - I don't see the point of the curry function. I understand currying is great, I even use it a lot, but I don't see the benefit of converting a "normal" function to a curried one.

I mean, when I need currying, I just write my code like that.

Everything else looks pretty awesome!

Have you looked at underscore (http://underscorejs.org/) a lot of the functionality seems to overlap (and it's what I've been using for a long time to make functional javascript a bit more palatable)


I'm with you on the currying thing. The ability to curry functions doesn't enable you to do anything you couldn't already do better and with more flexibility by writing the full anonymous function.

Their example breaks down the moment your argument order matters: https://gist.github.com/3052264


They seem to simply be missing flip.

  // Lets curry subtract instead of add
  var subtract = Udon.curry(function(a, b) {
      return a - b;
  });

  var five_minus = subtract(5);
  var minus_five = Udon.flip(subtract)(5)


Yes, although there is one subtlety here: should `flip` return a curried function, or an uncurried function? This isn't an issue in Haskell since it only has unary functions.


How about both?

  Udon.flip = function(f) {
      return function(x) {
          if(arguments.length > 1) {
              return f(arguments[1])(x);
          }
          return function(y) {
              return f(y)(x);
          };
      };
  };


Haskell has tuples, so it could use an:

  (a, b) -> c
representation. If the uniform/canonical way to use functions is defined to be curried, then flip can simply return them curried.


> Haskell has tuples, so it could use an:

Which it does:

    curry :: ((a, b) -> c) -> a -> b -> c

    uncurry :: (a -> b -> c) -> (a, b) -> c


Yes, it does. This is true in Haskell too, of course.

    subtract :: (Int, Int) -> Int
    subtract (n, m) = n - m
    
    fiveMinus = (curry subtract) 5
    minusFive = ???
    
    subtract' = \n m -> n - m
    fiveMinus =   \m -> 5 - m
    minusFive =   \m -> m - 5


Yes, what does? Are you agreeing or disagreeing with my statement that currying is useless? I think you're agreeing.


To be honest, I completely agree with you. But it wouldn't be a JavaScript functional programming library if it didn't have the curry function.

I'm pretty familiar with Underscore, just wasn't happy with a couple of the design decisions, otherwise I would have just used that. Udon actually came out of various hacky functional JS libraries that I'd written over the years, and part of the motivation was to pull those together into a clean and coherent codebase.


Which of Underscore's design decisions didn't you like?


Oh, I think it was mainly trivial stuff. I'm afraid I don't remember the specifics, this was two years ago.


Why would I go with Udon (eager, functional) rather than underscore (also eager and functional, but more popular) or wu.js (lazy & functional)?


You probably shouldn't. :)


Looks awesome, mainly because all the function names tie up with Haskell, but what's the difference between this and underscore.js?

Also, you might be interested in Roy: http://roy.brianmckenna.org


It has a few more functions that operate on lists, but it's missing a lot of the general utility functions that Underscore has.

Roy looks interesting, thanks for the link.


I'm in the process of writing my a functional utility library myself, and having thought about these things for a while, I think letting everyone curry things themselves make things less intelligent.

For example:

Udon.elem(2, [1, 2, 3]) === true;

As it stands this is simply an accessor for Array.prototype.indexOf. If people know how to use that, this alias is relatively useless unless you want to compose. And even then, you could always compose around the expression, or with a method on the prototype of the result (Number in this case).

However, it's very useful to have a curried version of it so that it can be used in filters:

[1,2,3,4,3,2].filter($.elem([2,4])); // [ 2, 4, 2 ]

This maintains your current javascript indexOf usage, but allows a higher order use that I think should be the default for this function.

If you want you JavaScript to look more like Haskell, then your way makes more sense, but having to write Udon.curry everywhere sort of takes away from that cleanliness you are trying to recreate. I prefer to not mess with existing semantics too much because of it. For reference, here's my library: https://github.com/clux/interlude#usage


> having to write Udon.curry everywhere sort of takes away from that cleanliness you are trying to recreate.

This is true, and I considered your approach when writing functions like `elem`. There were two reasons for this. Firstly, I wanted to avoid introducing more closures than necessary. Secondly, I wanted to keep the source code reasonably transparent (if admittedly rather terse).

There is definitely a place for your approach, and it may even be the better one in practise. It's hard to say without writing large projects in both.


Is it standard practice in other languages / libraries to have the function argument first and the data argument second, in e.g. map and filter?

In usual PHP fashion, array_map and array_filter take arguments in different orders. From all the jQuery i write it seems more natural to put the function argument last ($.get, $().bind, etc).


Putting the data argument second is nice when combined with currying. For example:

  var bigNumbers = Udon.curry(Udon.filter)(function (number) {
      return number > 5;
  });
  bigNumbers([3, 1, 4, 1, 5, 9]);  // [9]
  bigNumbers([2, 7, 1, 8, 2, 8]);  // [7, 8, 8]


The other reason things work this way in functional languages might be the way your nesting tends to work out. For example, it would be common in Clojure to write something like:

    (set-result-to (do-this-to (do-that-to (do-the-other-thing-to (map map-function some-input-data)))))
If the order of arguments to map was reversed, your map function would be the only one messing up the right-to-left flow of your data.

/so not a functional programmer


I think the order of arguments comes from the use of partial application. In Haskell you can write:

  doubler :: Num a => [a] -> [a]
  doubler = map (2 *)


If you are interested in this you might want to check out http://gkz.github.com/prelude-ls/ - it has almost all the functions included in Udon as well as about 60 more based off of Haskell's Prelude module.


That's very cool, although you have to use another dialect of JavaScript if you want to modify the source code, with all the disadvantages that entails.


True, but taking that aside and focusing on practicality, both underscorejs and Preludejs together provide one heck of a toolbox. I really enjoy functional programming a great deal and have been using it every which place I can the last several years. I totally understand that this is your take on the subject matter and so far so good, though the docs and usage for me are strange. If you have the goal of mass adoption you'll need to convince people like me why it needs adding to the toolbox.


I don't think one can ignore that and also focus on practicality. Using these JS dialects is a practical issue. Learning them and remembering how they work imposes a certain amount of overhead, albeit a reasonably small one. Of greater concern is maintaining projects written in these dialects: what if the support around key parts of your toolchain goes away? Of course they're open source, so you can maintain them yourself—but that may be a lot of work, and will rarely be the highest priority. Ultimately it may be more effort than it's worth, even if it's a lot more enjoyable to write than vanilla JavaScript. I'm not saying that this will always be the case, but it's an issue that does need to be addressed before jumping in and using them on a major project.

Anyway, Udon was really just a personal library which I tidied up and documented in case it might be of use to somebody. I think this is generally good practise: it encourages one to write clean, maintainable code, some documentation, and a test suite with reasonable coverage. In principle one should always do these things, but somehow putting it somewhere public makes it more likely that they'll actually happen. I was actually quite surprised to see it on Hacker News today, since I first wrote it two years ago and last made any changes to the source code over a year ago.

In other words, I'm not exactly falling over myself to promote it to the wider world or make it fit people's needs other than my own. Patches to increase the functionality of the library or improve the documentation would of course be welcome, and several good suggestions have already been made on this thread. But I'm not too concerned about convincing people here or elsewhere that they should be using it. In fact, they probably shouldn't: as you and others have said, most or all of its functionality is provided by other libraries which are far more actively maintained (this is really just a corollary of my first point).

However, I would be interested in hearing what you think is strange about the documentation and usage.


It's an issue I see when things go public. Public then mainstream. As such mainstream users mean mainstream docs. So my evaluation was in those eyes. The most important thing to gain is refinement that can be learned for the next lib you may or may not start/contribute to.


Yeah and I 100000% thank you for contributing!!!

It is a great contribution to the community and personally, regardless of my or other's personal technicalities.


I'd probably throw in concatMap, head, tail and the various scan functions for lists. Maybe init and last as well, although I never really use them.

As far as functions go, uncurry would be neat and create a nice symmetry along with curry.


Patches for these would be welcome; I did mean to add them, but I got distracted by other projects, and then by my PhD.


i don't see any advantage of using it




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

Search: