1

I have a question and it is quite simple. Here are the details:

  • I want to pass an options object into my mainFunction and some other functions inside of it, i.e. someFunctionCall and anotherFunctionCall. This options object is very much for configs. Because my algorithm has many different settings, I am much better off passing the whole object rather than certain properties.

  • Additionally, within my mainFunction, I compute and assign to a variable (hardToComputeThing). This computation is computationally intensive. So when my config states that it doesn't need it for the whole of mainFunction then we shouldn't use it.

The tricky part is when I use my internal function, like someFunctionCall. Assuming that someBool=false(from options), which means that hardToComputeThing=null. I am passing some empty variable inside each object and depending on my config object (options) to inform each internal function on whether I should use that empty object or not. I know it seems a little funky, but the great thing I love about using options is that I can still make use of other keys in options and it makes explicit what my current config is.

So my question is: Is this good practice? If not, than what should I do? Is it better to pass {x,y,hardToComputeThing} as a functional argument and handle for the missingness of hardToComputeThing, e.g. banana= (hardToComputeThing)? hardToComputeThing[0]*f(apple): f(apple)?

Below is a some pseudocode to illustrate my problem.

const mainFunction = ( x, options) = {

  const {someBool} = options

  const hardToComputeThing = (someBool)? computeTheThing(): null

  someFunctionCall(x,y,hardToComputeThing, options)

  anotherFunctionCall(z,hardToComputeThing, options)

}

const someFunctionCall = (x,y,hardToComputeThing,options)=>{
  const {someBool} = options


  let banana;
  if(someBool){
    banana = hardToComputeThing[0]* f(apple)
    ... // Some big piece of code
  }else{
    banana = f(apple)
  }

}



1
  • I don't think this is "funky" at all - I do similar all the time (in Javascript,). Either way is fine, whatever feels best to you. Rik also has a nice answer.
    – user949300
    Commented Apr 10, 2020 at 16:34

2 Answers 2

2

Instead of setting hardToComputeThing to null when someBool is false, you could give hardToComputeThing a default implementation which doesn't do anything. That way you can pass the hardToComputeThing to the other functions who can use it without additional null checks.

This pattern is known as the null object pattern.

1
  • In the OPs example code, returning [1] works perfectly as a "null object". In real code it might be harder. Good answer!
    – user949300
    Commented Apr 10, 2020 at 15:55
0

Another possibility would be to use lazy evaluation: You compute the hardToComputeThing if and when it is needed (and, probably, you cache the value, then, in case it is needed, again):

const mainFunction = ( x, options) = {
  let hardToComputeThing = {
     cachedValue: null,
     get: function() {
       if (this.cachedValue === null) this.cachedValue = computeTheThing();
       return this.cachedValue;
     }
  }
  someFunctionCall(x,y,hardToComputeThing, options)
  anotherFunctionCall(z,hardToComputeThing, options)
}

const someFunctionCall = (x,y,hardToComputeThing,options)=>{
  if(whateverCondition){
    let banana = hardToComputeThing.get()[0]* f(apple);
    ... // Some big piece of code
  } else {
    banana = f(apple)
  }
}

The main advantage is that you will not need to duplicate the logic for whateverCondition (which, I assume, might be non-trivial). The other answer, by Rik D, has the same advantage, but keeps the logic for whateverCondition in mainFunction() while my answer would move it to someFunctionCall(). No idea what would be more natural/convenient in your use case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.