Is it possible to define an infix function?

后端 未结 5 959
不思量自难忘°
不思量自难忘° 2021-02-08 09:21

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



        
相关标签:
5条回答
  • 2021-02-08 10:06

    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.

    0 讨论(0)
  • 2021-02-08 10:07

    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 讨论(0)
  • 2021-02-08 10:09

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

    It's not possible in vanilla JS.

    It's not possible in CoffeeScript.

    0 讨论(0)
  • 2021-02-08 10:09

    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.

    0 讨论(0)
  • 2021-02-08 10:22

    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));

    0 讨论(0)
提交回复
热议问题