What is the optimal parameter order when utilizing pre-curried functions and composition in JavaScript?

We have a simple, mathematically curried function for subtracting numbers:

function sub(x) {
  return function (y) {
    return x - y;
  };
};

sub(3)(2); // 1

The function signature matches the obtained result. However, when function composition comes into play, things change:

function comp(f) {
  return function (g) {
    return function (x) {
      return f(g(x));
    };
  };
};

function gte(x) {
  return function (y) {
    return x >= y;
  };
};

comp(gte(2))(sub(3)) (4); // true

In function composition, the last parameter of each function is crucial as it receives the value returned by the previous function. In our example, 4 - 3 >= 2 would give false. The actual computation is: 2 >= 3 - 4, resulting in true.

To achieve the desired outcome, we can easily tweak sub and gte:

function sub(y) {
  return function (x) {
    return x - y;
  };
};

function gte(y) {
  return function (x) {
    return x >= y;
  };
};

comp(gte(2))(sub(3)) (4); // false

Now, directly calling functions gives unexpected values:

sub(3)(2); // -1 (expected 1)
gte(2)(3); // true (expected false)

We could switch arguments for each call or create partially applied functions:

function flip(f) {
  return function (x) {
    return function (y) {
      return f(y)(x);
    };
}

flip(gte)(2)(3); // false

var gteFlipped = flip(gte);
gteFlipped(2)(3); // false

Both approaches are cumbersome and not very readable.

Is there a preferred parameter order? Or a way to use both, depending on requirements (similar to Haskell's left/right sections for operators)?

A solution must consider that only unary functions are used!

Answer â„–1

If you're looking to partially apply operators without the need for verbose code like:

var gte2 = function (x) { return x >= 2; };

That's a valid use case, focusing on "partially applying operators".

The solution is straightforward. Simply create a curried function. Here's an example:

// var gte = y => x => x >= y; // ES6 syntax

var gte = function (y) {
    return function (x) {
        return x >= y;
    };
};

var gte2 = gte(2);

There are actually two ways to perform partial application with binary operators:

  1. Partially apply the operator to the left argument.
  2. Partially apply the operator to the right argument.

This leads us to two key questions:

  1. Which argument should be defaulted when partially applying the operator?
  2. How can we partially apply the operator to the other argument?

One thing we can agree on is that providing both arguments to the operator doesn't make sense.

// Instead of writing:

add(2)(3)

// You can simply write:

2 + 3

We create curried operator functions primarily for partial application.

Therefore, providing both arguments to the function simultaneously is counterintuitive.

What does this mean in practice? It implies that:

  1. We have the flexibility to choose any argument order.

    // Both options are valid:
    
    var sub = x => y => x - y;
    
    // And:
    
    var sub = y => x => x - y;
    
  2. The function only needs to make sense with one argument.

    // For instance:
    
    var sub = y => x => x - y;
    
    // This works:
    
    sub(1) // interprets as (x => x - 1)
    
    // However, this doesn't work intuitively:
    
    sub(2)(3) // expected (2 - 3) but it calculates (3 - 2)
    
    // Yet, it only needs to make sense given one argument.
    

So, which argument order is preferable? It all depends.

  1. For commutative operations, argument order is irrelevant.

    Both addition and multiplication, for example, are commutative. Hence, a + b = b + a and a * b = b * a.

  2. Non-commutative operations typically benefit from a right-to-left argument order as it enhances readability during partial application.

    For instance, lt(2) usually means x => x < 2, not x => 2 < x.

    Why is this common? In JavaScript, function names precede the argument, so name(arg) reads naturally as

    x => x name arg</code rather than <code>x => arg name x
    .

  3. Though there are exceptions to the second guideline. Notably, division:

    div(10) // suggests divide 10 by x
            // not divide x by 10
    

    Determining the correct argument order for such cases may vary, though left-to-right seems more intuitive to me.

Here are several curried operator functions to consider:

// Commutative operators:

var add = x => y => x + y;
var mul = x => y => x * y;

// Right-to-left operators:

var lt  = y => x => x < y;
var gt  = y => x => x > y;
var lte = y => x => x <= y;
var gte = y => x => x >= y;
var sub = y => x => x - y;

// Left-to-right operators:

var div = x => y => x / y;

Now, how do we partially apply these operators to the "other" argument?

The sole approach involves creating a new function with reversed argument orders.

Fortunately, creating new functions for every operator isn't necessary:

  1. For commutative operators, the argument order is interchangeable. Therefore:

    flip(add) = add
    flip(mul) = mul
    
  2. Relational operators don't require extra functions either:

    flip(lt)  = gt
    flip(gt)  = lt
    flip(lte) = gte
    flip(gte) = lte
    
  3. Only flipped operator functions for sub and div are essential:

    var subFrom = x => y => x - y; // subFrom(5) corresponds to (y => 5 - y)
    var divBy   = y => x => x / y; // divBy(10) represents (x => x / 10)
    

In conclusion, trust your intuition when determining the best course of action.

Answer â„–2

After reviewing your composition, here is my interpretation:

comp(gte(2))(sub(3)) (4);

gte(2) = function(y) { return 2 >= y; } // (x = 2)
sub(3) = function(y) { return 3 - y; } // (x = 3)

// Therefore:
comp(gte(2))(sub(3)) = function(x) {
    var f = function(y) { return 2 >= y; };
    var g = function(y) { return 3 - y; };
    return f(g(x));
};

// Call with (x = 4):
x = 4
g(4) = 3 - 4 = -1
f(-1) = (2 >= -1) = true

In summary, it appears that your assumptions may be incorrect. There could be a misunderstanding on your part, but pinpointing it exactly is challenging for me. I believe that the approach taken in this JavaScript code is unnecessarily complex and can lead to confusion, but ultimately, it's just my perspective.

Answer â„–3

This reply is based on Aadit's response.

In the world of Javascript, there exists a necessity for fully applied curried operator functions, especially when they are treated as First Class Citizens:

function between(ops) {
  return function (left) {
    return function (right) {
      return function (n) {
        // Utilizing native Javascript operators here
        // However, passing these operators to a function poses a challenge due to them not being First Class.
        return ops[0](left)(n) && ops[1](right)(n);
      };
    };
  };
}

function lte(y) { return function (x) { return x <= y; }; }
function gt(y) { return function (x) { return x > y; }; }

between([gt, lte])(2)(4)(4); // true
// Evaluates as: gt(2)(4) && lte(4)(4) === true; (confusing)

The between function may seem absurd, but it demonstrates that there is practicality in employing fully applied curried operator functions within Javascript. There are likely numerous other scenarios where this technique could be beneficial.

Aadit rightly points out that something like sub(2)(3) goes against the core concept of currying!

So, what would a viable solution entail?

  1. All curried operator functions must adhere to a right-to-left argument order
  2. An additional function should be introduced to indicate atypical usage when supplying all arguments to a curried function simultaneously

Introducing uncurryOp:

// Designed for all operator functions
function uncurryOp(f) {
  return function (x, y) {
    return f(y)(x);
  };
}

uncurryOp(gt)(2, 4); // false (intuitive)

While this solution isn't ideal, I believe there isn't one due to the absence of First Class and partially applicable operators in Javascript.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When there is no content in the responseText, AJAX will display an error

I've encountered an issue while trying to fetch data using AJAX. The problem lies in receiving an empty responseText. Here's the code I'm working with: JavaScript: function getFounder(id) { var founder = ""; $.ajax({ ...

Is it possible to bundle Live using Angular 2 and SystemJS-builder, even when attempting to load modules from node_modules?

I'm having a bit of trouble transitioning my angular 2 application into production. It seems to be searching for scripts within the node_modules directory. I'm fairly new to angular 2 and despite spending half a day looking for a solution, I can ...

Change the value of input on onClick event in React

Although this may seem simple, it is a little confusing to me. I'm having trouble understanding why the input value in the following case is not being reset. handleReset() { this.setState({ data: "" }); } <button onClick={this.handleReset}>R ...

Is there a way to ensure that an external file function completes before proceeding with the main code execution?

In my project, I have two key JavaScript files: main.js and load.js. Within load.js, there are two essential functions - getData, which makes an AJAX post call, and constHTML, which appends data based on the JSON array returned from the AJAX call. This app ...

Why is the JS Component failing to appear on the page?

My component is not rendering on the page. Despite no errors or warnings, I have logged the gameData value and confirmed that all data is correct. Below is the function being exported: export default function AllGameDeals( gameData ){ const dealArr = ...

I am looking to switch from a hamburger menu to a cross icon when clicked

Hey there, I'm currently working on a Shopify website and trying to make the hamburger menu change to a cross when clicked. I've been experimenting with different methods, but unfortunately haven't had any success so far. I have two images â ...

Receiving unexpected results when returning a function within a React hook

I'm currently working on developing a custom React hook that will provide users with a function to execute. This hook is designed to generate a function internally. Check out this simplified example // fetch.js import { useEffect, useState} from &qu ...

The functionality of save() is not compatible with mongoose.Schema

const Information = require('./Models/Information'); ... let sampleData = new Information( dataSample ); sampleData.save( function ( error ){ console.log('testing); if ( error ) { console.log('Error occurred while saving Informa ...

The value of Vue.js props appears as undefined

It appears that I may be misunderstanding how props work, as I am encountering difficulty passing a prop to a component and retrieving its value, since it always shows up as undefined. Route: { path: '/account/:username', name: 'accco ...

Utilize the provided parameter within a JavaScript function

<script type="text/javascript"> function updateTrackName(trackNum) { document.form1.track_(track_number)_name.value=document.form1.track_(track_number)_parent_work.value; } </script> To modify the line inside of the parent_wor ...

Create an onClick function that can direct you to a specific hyperlink without triggering a new browser window to open

Material UI is being used and a home icon has been imported into the navbar as shown below <Home edge="start" color="inherit" aria-label="home" onClick={event => window.location.href='/ <Home fontSize="lar ...

Encountering a Jquery 404 error while attempting to locate a PHP file through Ajax requests

I've been struggling with this issue for the past couple of hours, but I just can't seem to get it fixed. Here's my current file setup: classes -html --index.html --front_gallery.php -javascript --gallery.js I've been following a tuto ...

Whenever I attempt to incorporate the 'var' keyword within a for loop alongside setTimeOut, I encounter unusual output

Here's the code snippet I'm working with: for (var i = 1; i <= 5; i++) { setTimeout(function () { console.log(i); }, 1000); } I'm confused about the output of this code. When I run it, I see the number 6 printed in the c ...

Setting the axios baseURL configuration globally in Nuxt.js

When starting a new project, you can use the following command: npm init nuxt-app <project-name> To set up the baseURL for axios, refer to this guide: npm install @nuxtjs/axios In your nuxt.config.js file: modules: [ '@nuxtjs/axios' ...

My custom function is not invoking the Firebase function createUserWithEmailAndPassword

The function createUserWithEmailAndPassword is not being triggered within the SignUpUser function when the onClick event occurs. However, it works when I use onClick={signUpUser(email,password)} import React from 'react'; import styled from &apo ...

What is the best way to configure AngularJS for optimal integration with Google Maps services and functionalities in an "angular way"?

I'm currently working on a new app that utilizes the Google distance matrix, directions service, and various map features such as custom markers. I'm curious - where do you think is the best place to house all of this different functionality? Sho ...

Tips for retaining the value of a variable in JavaScript

I am a page that continuously redirects to itself, featuring next day and previous day arrows. Upon loading, the page always displays the new day. The days are stored in a localStorage as follows: for(var i = 0; i < status.length; i++) { local ...

Tips for dynamically loading JSON data in Pug and JavaScript by implementing scroll functionality

This is my pug file for specificBike in allBikeData .bikeshop-card.border.border-dark.bg-white.m-3.p-4 li img.shop-image.border.border-dark(src=specificBike.image, onerror="this.onerror=null; this.src=&a ...

Wait to fade in until the fade out process is completely finished

When I click on a button, the old box fades out and the new box fades in. However, I want to ensure that the .fadeIn() only happens once the .fadeOut() is complete. This will prevent two containers from being displayed simultaneously. Any suggestions on ...

Issue in d3.js: bisector consistently returning zero

http://jsfiddle.net/rdpt5e30/1/ const data = [ {'year': 2005, 'value': 771900}, {'year': 2006, 'value': 771500}, {'year': 2007, 'value': 770500}, {'year': 2008, 'value&apos ...