EDkatzen
EDkatzen

Reputation: 85

returning object that returns functions

I need to write a function in JavaScript that takes a number and returns an object that returns chainable functions (without using OOP).

Example:

func(3).not().not().equals(4)

would outputs false.

And:

func(5).equals(5)

would output: true

This is the code I have written:

const func = (obj) => { 
  const obj2 = {
    not: () => {
      return !obj
    },
    equals: (num) => {
     return obj === num
   }
 }
  return obj2 
}

It works when I call func(3).not() or func(5).equals(5), but doesn't allow me to chain the functions so calling func(5).not().equals(5) returns an error saying that this is not a function.

What am I not seeing here?

Upvotes: 0

Views: 130

Answers (4)

Bergi
Bergi

Reputation: 664395

To return another object with the same methods that wraps the new value, simply call func again:

const func = (obj) => { 
  const obj2 = {
    not: () => {
      return func(!obj)
//    ^^^^^^^^^^^^^^^^^
    },
    equals: (num) => {
      return obj === num
    }
  }
  return obj2 
}
console.log(func(3).not().not().equals(4))
console.log(func(5).equals(5))
console.log(func(3).not())

Upvotes: 1

Aadit M Shah
Aadit M Shah

Reputation: 74204

That's a very weird way to compose functions. Let's think about what's actually happening.

func(3).not().not().equals(4)

// is equivalent to

not(not(equals(4)(3)))

// where

const not = x => !x;

const equals = x => y => x === y;

The simplest way to implement this chain would be as follows.

const equals = x => toBool(y => x === y);

const toBool = func => ({
    not: () => toBool(x => !func(x)),
    func
});

const example1 = equals(4).not().not().func(3);
const example2 = equals(5).func(5);

console.log(example1); // false
console.log(example2); // true

However, this is a forward chain. You want a backward chain. Unfortunately, there's a problem.

  1. In a forward chain .func(x) marks the end of the chain.
  2. In a backward chain .equals(x) marks the end of the chain.

This means that in a backward chain, you wouldn't be able to write the following expression.

func(3).not().not().equals(4).add(1)

// expected to be equivalent to

not(not(equals(4)(add(1)(3))))

// but actually equivalent to

not(not(equals(4)(3))).add(1)

// which evaluates to

false.add(1)

On the other hand, you would be able to do this quite easily using a forward chain.

const id = x => x;

const toNum = func => ({
    add: x => toNum(y => x + func(y)),
    equals: x => toBool(y => x === func(y)),
    func
});

const toBool = func => ({
    not: () => toBool(x => !func(x)),
    func
});

const { add, equals } = toNum(id);

const example1 = equals(4).not().not().func(3);
const example2 = add(1).equals(4).not().not().func(3);

console.log(example1); // false
console.log(example2); // true

By the way, this is an object-oriented design pattern even though it doesn't make use of classes.

My advice would be to write plain old functions.

const add = (x, y) => x + y;

const equals = (x, y) => x === y;

const not = x => !x;

const example1 = not(not(equals(4, 3)));
const example2 = not(not(equals(4, add(1, 3))));

console.log(example1); // false
console.log(example2); // true

The simplest solutions are usually the best. Source: Occam's razor.

Upvotes: 1

Ivan
Ivan

Reputation: 40638

You can use a closure to store both the initial input and the state of the operation:

const func = (input) => {
  let not = false
  const obj = {
    not: () => {
      not = !not
      return obj
    },
    equals: (num) => {
      return not ? input !== num : input === num
    }
  }
  return obj;
}

console.log(func(5).not().equals(5))
console.log(func(5).not().not().equals(5))
console.log(func(5).not().equals(4))
console.log(func(5).not().not().equals(4))

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386560

You could take an object for return as interface and store value and negation.

var object = {
        func: function (value) {
            object.left = value;
            return object;
        },
        not: function() {
            object.negation = !object.negation;
            return object;
        },
        equals: function (value) {
            var result = value === object.value;
            return object.negation ? !result : result;
        }
    },
    func = object.func;


console.log(func(3).not().not().equals(4));

Upvotes: 0

Related Questions