20 comments

[ 2.7 ms ] story [ 65.9 ms ] thread
Sigh ...

  module.exports = function (fn) {
    return function (first) {
      fn(first);
    };
  };
There's also `butt` if you have more complicated needs. Feel free to use it.

https://github.com/bevacqua/butt

Why not allow an optional 2nd argument that defaults to 1 on your `but` library to do this?
That's what butt does! `but` doesn't do this because to be honest it's quite rare that you'll need it. and it's also butt-ugly to do that.
Why complicate things? If it means so much to you, you can just

  var butt = require('butt');
  var but = function(fun) {
    return butt(fun, 1);
  };
or even

  var but = function(fun) {
    return function(arg) {
      return fun(arg);
    };
  };
Can anyone explain to me why I couldn't write

  n.forEach(console.log);

Just tested on Chrome, it gives me an error

  TypeError: Illegal invocation
When you use `console.log` that way it gets `this` set to `null`. When you call `console.log("hello")`, `this` is set to `console`. Your code would work if you set an explicit `this`.

    n.forEach(console.log.bind(console));
I don't think there's any reason for this library to exist after you understand `this` semantics but I may be missing something.

e: was, in fact, missing something. Apologies for the disparaging comment, see replies.

That's one, the other is that many of those functional methods tend to mess with functions that take more than one argument when you want to use the first one, e.g parseInt.
Well it's not just about `this` but the arguments to pass to console rather. In this case:

    n.forEach(console.log.bind(console));
forEach is passing console all of its callback arguments, resulting in logging of keys, values, and array.

EDIT: Forgot array

Ah, I understand. It reminds me of how the `cut` function could be used in Scheme.
Additionally, this will log all the passed arguments (value, index, and array being traversed), which typically isn't what you want:

    [1, 2, 3].forEach(console.log.bind(console));
    > 1 0 [1, 2, 3]
    > 2 1 [1, 2, 3]
    > 3 2 [1, 2, 3]
Convenience, I suppose; I had a few headaches when I first transitioned to Javascript and discovered the odd signatures its collection functions' callbacks are expected to have.

  n.forEach(console.log);
Works on firefox. Chrome treats console.log differently from other functions for reasons unknown.

but really, this library has no reason to exist

     function butter (){                                                                                                                                                                                                                                                                                      
       fn=arguments[0];                                                                                                                                                                                                                                                                                    
       return function(){                                                                                                                                                                                                                                                                                  
         var args = [];                                                                                                                                                                                                                                                                                     
         for( var n=1; n<arguments.length; n++ ) {                                                                                                                                                                                                                                                           
            args.push(arguments[n]);                                                                                                                                                                                                                                                                          
          };                                                                                                                                                                                                                                                                                                 
        return fn.apply(this,args)                                                                                                                                                                                                                                                                          
       }                                                                                                                                                                                                                                                                                                  
     }

edit: I know my loop is totally anachronistic, but I'm short on time for testing this.

  function butter(fun, count) {
    count = count || 1;
    if (count < 1) {
      throw new Error('Can't butter no toast');
    }
    return function() {
      return fun.apply(this, Array.prototype.slice.call(arguments, 0, count));
    }
  }
This is a dumb question, but I feel like maybe I'm failing to pick up on something with respect to the necessity of the count parameter. It doesn't seem like it should be necessary, but both 'butt' and your version here use it. What is it for?
It specifies how many arguments the returned function should take; in my implementation, it defaults to 1 (so you can use it like "but"), but accepts other values (so you can use it like "butt").

The reason why it's necessary to specify how many arguments are of interest is because

Of course, since this is just common or garden partial application in a language with first-class functions and a liberal attitude toward signatures, it could be trivially generalized thus:

  function partial(fun) {
    var applied = Array.prototype.slice.call(arguments, 1);
    return function() {
      var args = applied
        // clone the already applied args, so we don't mistakenly modify when we...
        .slice()
        // ...tack on the args we received in this function's call
        .concat(Array.prototype.slice.call(arguments));
      return fun.apply(this, args);
    };
  }

  var addThreeNumbers = function(a, b, c) {
    return a + b + c;
  };

  var onePlusTheseTwo = partial(addThreeNumbers, 1);

  var threePlusThisOne = partial(onePlusTheseTwo, 2);

  assert(addThreeNumbers(1, 2, 3) === 6);
  assert(onePlusTheseTwo(2, 3) === 6);
  assert(threePlusThisOne(3) === 6);
  // -> all true
It only looks like wizardry if you're not used to dealing with higher-order functions, wherein I think is meant to lie the joke, if any.
I don't encounter higher-order functions very often, and I certainly am not mentally flexible with them. Nonetheless, I have heard of partial application, yes. I understood what the count parameter was doing.

What I was having trouble understanding was why it was being used; specifically, why 'but' would have a strict arity of one in the first place, and why a second function (specifying arity) would be necessary. It doesn't follow very clearly from the examples given at all.

edit: ...was this whole thing actually a joke? I thought he was serious.

Oh! Sorry, no, that's because ECMAScript (Javascript)'s historical baggage and lax attitude toward arity produce a combinatorial explosion of stupidity.

See, you can't just

  ['1', '2', '3'].map(parseInt)
because:

0) All Javascript functions are variadic; the signature specified at definition time is largely advisory, and has semantic meaning only in that the language will automatically declare and bind named arguments. Arguments named in a function's signature, but not passed at call time, are undefined; arguments not named in a function's signature, but passed at call time, are not automatically bound to named variables, but can be accessed via the arguments binding, which is a unique object magically bound within a function's scope, but which will be overridden by a named argument "arguments" if one exists in the signature,

and

1) Array.map and friends pass their callbacks three arguments: the value of the element, the index of the element in the array being mapped over, and a reference to the entire array being mapped over,

and

2) Number.parseInt requires one argument, the value to cast to integer, and accepts an optional second argument, specifying a radix between 2 and 36 inclusive.

So our naïve mapping above expands to the following series of calls:

  parseInt('1', 0, ['1', '2', '3']);
  // -> 1
  parseInt('2', 1, ['1', '2', '3']);
  // -> NaN
  parseInt('3', 2, ['1', '2', '3']);
  // -> NaN
tl;dr: None of this would be necessary in a language whose collection methods did sane things. Since Javascript is not such a language, this is what we have to do.

...I'm not actually sure this whole thing was a joke, but it reads to me a little like it might've been written with the sort of smirk you tend to see academic FP purists directing at industry engineers.