0

Try to use functional programming to create an object with external functions to reduce memory usage.

The function is

//increment no of test cases
function incrNoOfTestCases(inputObj){
    let hits = inputObj.hits;
    console.log(`function says: ${hits}`)
    return {...inputObj, hits: (hits || 0) + 1};
}

The creator function is

const test = function(testDecription){
    let state = {hits:0};
    state.getState = ()=> testDecription;
    state.incHits = () => state = incrNoOfTestCases(state);
    state.getHits = () => state.hits || 0;
    return state;
}

When I do the following test, I can change the hits by assigning a property with to the function.

test1.incHits().hits=10;  //mutable!!
console.log(test1.getHits());  //gives 10
console.log(test1.incHits().hits);    //gives function says: 10 and then 11
test1.hits=20; //immutable
console.log(test1.getHits());  //gives 10

I tried various alternatives, finally came up with declaring the function to increment the testcases in the creator function. I am looking for an explanation why the property is mutable not for a working case.

In the first version the function was

function incrNoOfTestCases(inputObj){
    return {...inputObj, hits: (inputObj.hits || 0) + 1};
}

In this case I also expected the inputObj.hits not to be mutable by incrNoOfTestCases.hits, but wasn't either.

It seems JavaScript firstly assigns incrNoOfTestCases.hits to state before executing the function. Is this correct? Can you explain why?

  • In your third code block, what is `test1`? – T.J. Crowder Jan 02 '23 at 18:48
  • How and when is `test()` called? – Pointy Jan 02 '23 at 18:49
  • 1
    Assuming `test1` is the result of calling `test`, then sure, the object that `incHints` returns is mutable. Why wouldn't it be? Your code isn't doing anything to prevent the `hits` property of the object returned by `incrNoOfTestCases` (which `incHits` returns) from being modified. *"It seems JavaScript firstly assigns..."* If you're wondering what order things happen in, your best bet is to [use a debugger](https://stackoverflow.com/questions/25385173/) (more: https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). – T.J. Crowder Jan 02 '23 at 18:51
  • 1
    "*create an object with external functions to reduce memory usage*" - well your `test` function fails at doing that, it definitely does create new functions on every call and also puts them right on the object. – Bergi Jan 03 '23 at 15:10
  • Thanks all for commenting. Much appreciated. Forgot to include const test1 = test('test 1 ...'); as first line of my testl, sorry Crowder... What I realise now, is that state is returned by the creator function, thus test1 is from that point on the same and as any property public, thus mutable. But incHits is a property of test1, being a function is an object, it can have its own properties, but test1.incHits().hits =/= test1.hits. I expected the incrNoOfTestCases to use test1.hits iso test1.incHits().hits. – Bas Botman Jan 04 '23 at 16:57

1 Answers1

2

There is nothing functional about this code. In functional programming you don't want small logical units to handle their state independently. That's OOP. Using a closure is just the same as using a class if you mutate the value.

This is more functional although it probably doesn't work the way you would like.

const Test = (description, hits = 0) => ({
    getState: () => description,
    incHits: () => Test(description, hits + 1),
    getHits: () => hits
})

const test1 = Test('description')
const test2 = test1.incHits(); // incHits returns a new instance of Test
console.log(test2.getHits())

And this would have done the same thing

class Test {
   constructor(description, hits = 0) {
      this.description = description;
      this.hits = hits;
   }
   static of (description) { return new Test(description) }
   getState () { return this.description}
   incHits () { return new Test(this.description, this.hits + 1); }
   getHits () { return this.hits }
}

const test1 = Test.of('description');
const test2 = test1.incHits();

Yet another way to do it

const Test = (description, hits = 0) => ({ description, hits, type: 'Test' });
export const getState = ({ description }) => description;
export const incHits = ({ description, hits }) => Test(description, hits + 1);
export const getHits = ({ hits }) => hits;
export const of = (description) => Test(description);
import * from './Test'
const test1 = Test.of('description');
const test2 = Test.incHits(test1);
geoffrey
  • 2,080
  • 9
  • 13
  • Thanks Geoffrey, you set me to some more research, dived into https://crockford.com/javascript/private.html and his books again after your comments. My knowledge on public / private and ways to make objects was lacking. Finally followed Douglas using a function that returns an object to create an object (see JavaScript the good parts, chapter 5). It seems elegant and making the best usage of JavaScript to me. – Bas Botman Jan 04 '23 at 17:01
  • It is very good that you learn that stuff, but do realise it is 20 years old. The class syntax is a lot more straightforward. Private members are self-documenting, and in Typescript you even get `private` and `protected` keywords (although they only protect you from yourself). If you don't like the `new` keyword, consider using a static method like `Test.of` or a "factory" function which delegates object creation to the constructor and merely passes down the arguments. – geoffrey Jan 04 '23 at 18:59
  • Among the 3 means of construction listed in my answer, the first one is the worst performance-wise, because you create new functions each time you create an instance of `Test`. It is also not very readable because "methods" seem to have free variables, which they get from the closure. A class (in JS) makes it obvious because you must mention `this` and functions in the 3rd example take an argument. – geoffrey Jan 04 '23 at 19:08
  • In any case, take note that in functional programming objects don't maintain state: they are only bags of data. – geoffrey Jan 04 '23 at 19:10