Is it possible to define an infix function?
Asked Answered
T

6

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?

Teddie answered 10/9, 2012 at 11:31 Comment(6)
possible duplicate of Can I define custom operator overloads in Javascript?Krispin
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?Andean
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 (because it'd need type inferencing and so on, which goes against the idea of having just JS).Enthrall
@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.Enthrall
@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).Teddie
Define a higher order nfx() function à la https://mcmap.net/q/1013280/-haskell-39-s-infix-operator-for-javascriptLeucocytosis
C
27

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));
Coward answered 28/1, 2017 at 18:58 Comment(1)
Javascript has finally a composition operator: ['∘']. This is a great hack! +1Fulgurite
E
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.

Enthrall answered 10/9, 2012 at 11:46 Comment(1)
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.Teddie
S
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.

Spent answered 7/4, 2015 at 16:26 Comment(0)
H
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.

Hunker answered 19/9, 2016 at 4:8 Comment(0)
F
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.

Fulgurite answered 21/5, 2017 at 13:4 Comment(0)
S
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.

Sonni answered 19/4, 2022 at 18:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.