3

I am trying move more towards functional programming in my javascript applications. I currently use the library ramda as a base lib for this.

My desire:

  1. Create a function findWithId(id, list) which returns the item in the list with the property _id matching the input id.
  2. Make the implementation of it as short as possible, relying on existing code as much as possible.

Acheived so far:

My base is R.find which has this defenition find :: (a -> Boolean) -> [a] -> a | undefined I tried some different solutions shown below:

//Using ramdajs (ramdajs.com)
var hasId = R.curry(function(id, item) {
  return item._id === id
});

//This doesn't work
var findWithId_v1 = R.find(hasId);

//This works but can I make it as short as v1?
var findWithId_v2 = function(id, list) {
  return R.find(hasId(id), list);
}

Question

Can I make findWithId_v2 as short as findWithId_v1 by using standard functional programming tools like compose, curry etc?

Plunker demo

http://plnkr.co/edit/xAYx15yk6lBzutdAL9HC?p=preview

Anderson Green
  • 30,230
  • 67
  • 195
  • 328
Ludwig Magnusson
  • 13,964
  • 10
  • 38
  • 53
  • __[Why over-complicate things?](http://plnkr.co/edit/NKcKGw2HApvSV71yhi9D?p=preview)__ – Cerbrus Oct 03 '14 at 11:36
  • Or even better, __[just iterate over the array with angular.](http://plnkr.co/edit/Csl0O8ixGkrLWorDameM?p=preview)__ – Cerbrus Oct 03 '14 at 11:39
  • 1
    That is not necessarily what I want to do. I want to understand FP and if I know this is possible then I can utilize it in many places. If I know it is not, then I wont be (constantly) bothered about considering whether I can improve my code or not. – Ludwig Magnusson Oct 03 '14 at 11:39
  • @Cerbrus After reading you second comment, I don't think you understand the question and or subject matter. – Ludwig Magnusson Oct 03 '14 at 11:40
  • Okay, _why_ do you want to do it with this library? Why do you want to _"move more towards functional programming"_? – Cerbrus Oct 03 '14 at 11:42
  • @Cerbrus: Just because `var findWithId = R.compose(R.find, R.propEq('_id'))` is so much more concise than `var findWithId = R.curry(function(id, list) { return R.find((function(item) { return item._id === id }, list); })` ? :-) – Bergi Oct 03 '14 at 12:08
  • Why use this library in the first place? Writing functions for the sake of functional programming hardly seems efficient. Especially if what you're doing can be replaced by a single `ng-repeat` statement. – Cerbrus Oct 03 '14 at 12:14
  • @Cerbrus This is not about angular!!! I use angular in my plunk to show the desired result. If you want to get introduced to functional programming, please check the video link. I am not responding to anything more in this particular discussion. https://www.youtube.com/watch?v=m3svKOdZijA – Ludwig Magnusson Oct 03 '14 at 12:22
  • Way to over-complicate logging the result o.O – Cerbrus Oct 03 '14 at 12:26
  • @Cerbrus: Agreed about the logging, but there are plenty of good reasons to use a higher-level library. (Of course as one of the authors of the library in question, perhaps I'm biased! :-) ) But the points-free solution in my answer is fairly simple and readable, once you know the constructs involved. Granted, `useWith` is still reasonably obscure, but it wasn't that long ago that people said the same about `map`. – Scott Sauyet Oct 03 '14 at 13:49

2 Answers2

3

Ramda does have a function that will help with this, one called useWith. If you can think of a better name, I'd love it. I've asked that before.

You can use is like this:

var findById = R.useWith(R.find, hasId, R.identity);

var find5 = findById(5);
find5([{id:3}, {id:5}]); //=> {id:5} 
findById(5, [{id:3}, {id:5}]); //=> {id:5} 

You don't have to supply R.identity above. This would also work, although I prefer to be explicit:

var findById = R.useWith(R.find, hasId);

useWith "accepts a function fn and any number of transformer functions and returns a new function. When the new function is invoked, it calls the function fn with parameters consisting of the result of calling each supplied handler on successive arguments to the new function"

Community
  • 1
  • 1
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • Wonderful. Being new to FP I only wonder if "useWith" is a generic concept or specific to ramda. – Ludwig Magnusson Oct 03 '14 at 13:45
  • 1
    I did [ask that question](http://stackoverflow.com/questions/17386706/) but no one seemed to have a good answer. Part of the issue is that with a language like Haskell, where every function takes only a single parameter, this is never a problem, and I think the question just leads to confusion. – Scott Sauyet Oct 03 '14 at 13:54
0

You're looking for the compose function:

compose :: (a -> b) -> (b -> c) -> (a -> c)
hasId :: p -> (i -> Boolean)
find :: (i -> Boolean) -> ([i] -> i | undefined)
compose :: (p -> (i -> Boolean))
           -> ((i -> Boolean) -> ([i] -> i | undefined))
           -> (p -> ([i] -> i | undefined))
compose find hasId :: p -> ([i] -> i | undefined)

So you'd do

var findWithId = R.compose(R.find, hasId)

Oh, and notice that you don't need a function expression for hasId either:

var hasId = R.propEq('_id');
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Does not seem to work, check updated plnkr (same link as in question). – Ludwig Magnusson Oct 03 '14 at 11:49
  • Plnkr doesn't work for me, but I'm still testing; you might be right – Bergi Oct 03 '14 at 11:50
  • Actually `findWithId(5)([{_id:3}, {_id:5}])` does seem to work for me – Bergi Oct 03 '14 at 11:51
  • Ah, but not findWithId(5,[{_id:3}, {_id:5}]). One function taking two arguments. You had a function taking one argument, that returned another function which also took one argument. – Ludwig Magnusson Oct 03 '14 at 11:54
  • Yes, because compose works with curried functions... You can see from the type signature that `compose find hasId` gives you a function that takes an `a` (= `p`), a returns a `c` - which is = `([i] -> i|undefined)` – Bergi Oct 03 '14 at 12:01
  • It seems there is no `uncurry` method as of now that can transform this back into a function that takes multiple arguments… – Bergi Oct 03 '14 at 12:02
  • What do you mean? Curried functions can take multiple arguments. – Ludwig Magnusson Oct 03 '14 at 12:28
  • Well, not this one, because it *doesn't know* that it is curried (returns a function). This "syntax sugar" of "multiple arguments" doesn't work here. – Bergi Oct 03 '14 at 12:38
  • I now understand what you meant. Did not know of uncurry before. – Ludwig Magnusson Oct 03 '14 at 12:50