11

Is it possible to define my own infix function/operator in CoffeeScript (or in pure JavaScript)? e.g. I want to call

a foo b

or

a `foo` b

instead of

a.foo b

or, when foo is global function,

foo a, b

Is there any way to do this?

Mariya Davydova
  • 1,353
  • 9
  • 14
  • possible duplicate of [Can I define custom operator overloads in Javascript?](http://stackoverflow.com/questions/4700085/can-i-define-custom-operator-overloads-in-javascript) – Mārtiņš Briedis Sep 10 '12 at 11:36
  • Well a foo b is nothing else than a(foo(b)) in coffeescript. But afaik you cannot define your own keywords. Maybe this could be a starting point. Btw: why would you want to do this? – Patrick Oscity Sep 10 '12 at 11:38
  • It's not possible to define your own operator in JS, and as far as I can tell from the coffeescript homepage, it isn't in coffeescript either. Of course, because Coffeescript just compiles to JS, and is open source, you could add this kind of functionality (but note that the CS folks have explicitly [said no to operator overloading](https://github.com/jashkenas/coffee-script/issues/727) (because it'd need type inferencing and so on, which goes against the idea of having just JS). – Gijs Sep 10 '12 at 11:39
  • @padde the problem is that inside `foo` you wouldn't know about `a`, would you? So it's not possible to actually do infix stuff... And typically `a` would be a value rather than a function, which is also problematic with the OP's example. – Gijs Sep 10 '12 at 11:41
  • @MārtiņšBriedis No, I've read this question, and I do not want to overload existing operators (well, I want to, but I know that this is impossible). – Mariya Davydova Sep 10 '12 at 12:11
  • 1
    Define a higher order nfx() function à la http://stackoverflow.com/a/17667716/461146 – Andrew De Andrade Jul 16 '13 at 03:55

6 Answers6

26

ES6 enables a very Haskell/Lambda calculus way of doing things.

Given a multiplication function:

const multiply = a => b => (a * b)

You can define a doubling function using partial application (you leave out one parameter):

const double = multiply (2)

And you can compose the double function with itself, creating a quadruple function:

const compose = (f, g) => x => f(g(x))
const quadruple = compose (double, double)

But indeed, what if you would prefer an infix notation? As Steve Ladavich noted, you do need to extend a prototype.

But I think it can be done a bit more elegant using array notation instead of dot notation.

Lets use the official symbol for function composition "∘":

Function.prototype['∘'] = function(f){
  return x => this(f(x))
}

const multiply = a => b => (a * b)
const double = multiply (2)
const doublethreetimes = (double) ['∘'] (double) ['∘'] (double)

console.log(doublethreetimes(3));
okdewit
  • 2,406
  • 1
  • 27
  • 32
  • 3
    Javascript has finally a composition operator: `['∘']`. This is a great hack! +1 –  May 21 '17 at 12:54
7

Actually adding this as an answer: no, this is not possible.

It's not possible in vanilla JS.

It's not possible in CoffeeScript.

Community
  • 1
  • 1
Gijs
  • 5,201
  • 1
  • 27
  • 42
  • I've read both of these texts before asking. In fact, I hoped that there is still some way, maybe not very pretty and widely used, but possible. – Mariya Davydova Sep 10 '12 at 12:15
3

You can with sweet.js. See:

  1. http://sweetjs.org/doc/main/sweet.html#infix-macros
  2. http://sweetjs.org/doc/main/sweet.html#custom-operators

Sweet.js extends Javascript with macros.

It acts like a preprocessor.

CMCDragonkai
  • 6,222
  • 12
  • 56
  • 98
2

This is definitely not infix notation but it's kinda close : /

let plus = function(a,b){return a+b};

let a = 3;
let b = 5;
let c = a._(plus).b // 8

I don't think anyone would actually want to use this "notation" since it's pretty ugly, but I think there are probably some tweaks that can be made to make it look different or nicer (possibly using this answer here to "call a function" without parentheses).

Infix function

// Add to prototype so that it's always there for you
Object.prototype._ = function(binaryOperator){

  // The first operand is captured in the this keyword
  let operand1 = this; 

  // Use a proxy to capture the second operand with "get"
  // Note that the first operand and the applied function
  //   are stored in the get function's closure, since operand2
  //   is just a string, for eval(operand2) to be in scope,
  //   the value for operand2 must be defined globally
  return new Proxy({},{
    get: function(obj, operand2){
        return binaryOperator(operand1, eval(operand2))
    }
  })
}

Also note that the second operand is passed as a string and evaluated with eval to get its value. Because of this, I think the code will break anytime the value of operand (aka "b") is not defined globally.

Community
  • 1
  • 1
Steve Ladavich
  • 3,472
  • 20
  • 27
2

Javascript doesn't include an infix notation for functions or sections for partial application. But it ships with higher order functions, which allow us to do almost everything:

// applicator for infix notation
const $ = (x, f, y) => f(x) (y);

// for left section
const $_ = (x, f) => f(x);

// for right section
const _$ = (f, y) => x => f(x) (y);

// non-commutative operator function
const sub = x => y => x - y;


// application

console.log(
  $(2, sub, 3),   // -1
  $_(2, sub) (3), // -1
  _$(sub, 3) (2)  // -1
);

As you can see I prefer visual names $, $_ and _$ to textual ones in this case. This is the best you can get - at least with pure Javascript/ES2015.

0

You can get close by function currying:

const $ = (a) => (f) => f(a);
const plus = (a) => (b) => (a+b);
const twoPlusThree = $ (2) (plus) (3);

But I still haven't figured out a neat way to compose this construction.

thesame
  • 103
  • 2
  • 9