Is there a way to provide the ultimate conversion method when chaining operations (like decreasing the map) in underscore.js?

(Really trying to name this question, so if anyone has any suggestions, feel free to.)

Let's say I wanted to do an operation like:

  • take array [1,2,3]
  • multiply each element by 2 (map): [2,4,6]
  • add elements together (reduce): 12
  • multiply the result by 10: 120

I can do it pretty cleanly in underscore using chaining like:

arr = [1,2,3]
map = (el) -> 2*el
reduce = (s,n) -> s+n
out = (r) -> 10*r

reduced = _.chain(arr).map(map).reduce(reduce).value()
result = out(reduced)

      

However, it would be even nicer if I could bind the "out" method, for example:

result = _.chain(arr).map(map).reduce(reduce).out(out).value()

      

This will now be a fairly simple addition to the library, like an underline. But my questions are:

  • Does this "out" method have a name in functional programming?
  • This already exists in underscore ( tap

    comes close, but not quite).
+3


source to share


2 answers


This question got me hooked. Here are some of my thoughts.

It looks like using the underscore.js method in "chain ()" mode is breaking away from the functional programming paradigm. Basically, instead of calling functions on functions, you call methods on an instance of the wrapper object in an OOP fashion.

I use underscore () chaining myself in there too, but this question got me thinking. What if it's better to just create more meaningful functions that can then be called in sequence without chaining () at all. Then your example will look something like this:

arr = [1,2,3]
double = (arr) -> _.map(arr, (el) -> 2 * el)
sum = (arr) -> _.reduce(arr, (s, n) -> s + n)
out = (r) -> 10 * r

result = out sum double arr
# probably a less ambiguous way to do it would be
result = out(sum(double arr)) 

      

Looking at real-world functional programming languages ​​(as well as .. much more functional than JavaScript), it seems like you can do the same in an even simpler form. Here's the same program written in standard ML. Note that calling the map with only one argument returns another function. There is no need to wrap this map into another function like in JavaScript.

val arr = [1,2,3];
val double = map (fn x => 2*x);
val sum = foldl (fn (a,b) => a+b) 0;
val out = fn r => 10*r;

val result = out(sum(double arr))

      

Standard ML also allows you to create operators, which means we can make a small "chaining" operator that can be used to call these functions in a more intuitive manner.

infix 1 |>;
fun x |> f = f x;

val result = arr |> double |> sum |> out

      



I also think this underscore.js chain has something similar to monads in functional programming, but I don't know much about them. Although, I feel that such a data processing pipeline is not something that you usually use for monads.

I hope someone with more functional programming experience can trick and correct me if I am wrong on any of the points above.

UPDATE

A little off topic, but one way to create partial functions might be:

// extend underscore with partialr function
_.mixin({
  partialr: function (fn, context) {
    var args = Array.prototype.slice.call(arguments, 2);
    return function () {
      return fn.apply(context, Array.prototype.slice.call(arguments).concat(args));
    };
  }
});

      

This function can now be used to create a partial function from any underscore function since most of them take input as their first argument. For example, now the sum function can be created as

var sum = _.partialr(_.reduce, this, function (s, n) { return s + n; });
sum([1,2,3]);

      

I still prefer arr | > double | > sum | > out out out (sum (double (arr))). The underscore () chain is nice because it reads in a more natural order.

+3


source


In terms of the name you are looking for, I think what you are trying to do is just a function application form: you have an underscore object, and you want to apply a function to its value. In underscore, you can define it like this:

_.mixin({
  app: function(v, f) { return f (v); }
});

      

then you can pretty much do what you asked:



var arr = [1,2,3];
function m(el) { return 2*el; };
function r(s,n) { return s+n; };
function out(r) { return 10*r; };

console.log("result: " + _.chain(arr).map(m).reduce(r).app(out).value()));

      

Having said all that, I think using traditional typed functional languages ​​like SML makes this look a lot slicker and gives a much easier syntax for composition of functions. Underscore makes a kind of jquery twist in functional programming and I'm not sure what I'm thinking about; but it's hard to make mistakes without checking at the static level!

+2


source







All Articles