8

I have a given function that takes, among other arguments, two optional arguments which may be functions. Both need to be optional, one will be a function, and one will either be a boolean or a function that returns a boolean.

// Obj.func(variable, String[, Object][, Boolean||Function][, Function]);
Obj.func = function(other, assorted, args, BoolOrFunc, SecondFunc) {
    // execution
};

Obj.func(
    [
        'some',
         'data'
    ],
    'of varying types',
    {
        and: 'some optional arguments'
    },
    function() {
        if(some condition) {
            return true;
        }
        return false;
    },
    function() {
       // do things without returning
    }
);

It is my desire that both functions (and several of the other arguments, if it matters) be optional, which means I have code in the function to determine which arguments the user intended to use.

Unfortunately, as both may be functions, and may be specified directly in the function call, I cannot simply use typeof or instanceof conditionals. However, since the first function, if it exists, will always return a boolean (and the second function will not return at all), one idea I had would be to check its return value:

if(typeof BoolOrFunc === 'boolean'
   || (typeof BoolOrFunc === 'function' && typeof BoolOrFunc() === 'boolean')) {
    // BoolOrFunc is either a boolean or a function that returns a boolean.
    // Handle it as the intended argument.
} else {
    // Otherwise, assume value passed as BoolOrFunc is actually SecondFunc,
    // and BoolOrFunc is undefined.
}

This works in principle; however, running typeof BoolOrFunc() executes the function, which causes a problem if the function does more than just return a boolean: that is, if the function passed as BoolOrFunc is actually meant to be SecondFunc. SecondFunc, in this case, is something of a callback function, and may perform actions, including DOM modifications, that I don't want to execute immediately.

For this reason, my question is: Is there a way to check if a function returns without executing it?

One thing I had considered was to call BoolOrFunc.toString(), then perform a Regular Expression search for the return value, something along the lines of…

if(typeof BoolOrFunc === 'boolean'
   || (typeof BoolOrFunc === 'function'
       && BoolOrFunc.toString().search(/return (true|false);/) !== -1)) {
    // BoolOrFunc is either a boolean or contains a return string with a boolean.
    // Handle it as the intended argument.
}

Note that the above code may not work as written: I've not actually built a test case for it, because, well, it seems exceptionally inefficient and potentially unreliable, and I figured someone here might have a more elegant solution to my quandary. That having been said, I figured I'd include it for discussion purposes.

3
  • 1
    As an anecdotal side note, this is pretty close to the Halting Problem (en.wikipedia.org/wiki/Halting_problem) which is undecidable over Turing Machines. :) Commented Sep 9, 2014 at 8:28
  • Heh, you bring up a good point. I'm probably expecting far too much from JS. :P Commented Sep 9, 2014 at 9:15
  • For the case that users call the form which giving a boolean and a function that returns a boolean, can you require instead that they pass in the arguments wrapped in an object e.g. Obj.func(myvar, mystring, myobj, {boolarg: true, funcarg: my_func_that_returns_bool}); or something like that, to make it unambiguous what it is. Then the user must ensure that my_func_that_returns_bool does as it claims Commented Sep 15, 2014 at 14:42

2 Answers 2

1

Meshaal made a prediction in the question:

"... one will either be a boolean or a function that returns a boolean."
"... first function, if it exists, will always return a boolean."

With this prediction the function is not a Turing Machine, because we know that it returns something. Surely it can't be done with a simple RegExp: just return !0 would break the example.

But if you parse the result of function.toString() with a parser being intelligent enough to find all possible return points, the problem should principally be solvable.

Sign up to request clarification or add additional context in comments.

Comments

0

Interesting question, if I understand correctly. First, I'd probably warn against having such open-ended arguments. I'm curious as to the use case.

But that said, it's easy enough to get around the problems you mention (which may not wholly solve your problem).

This works in principle; however, running typeof BoolOrFunc() executes the function...

That's easy enough to fix, I think. At first, just check to see if BoolOrFunc is a boolean or a function (just typeof BoolOrFunc, natch). If it's a boolean, I assume we're fine. Either SecondFunc is missing (undefined) or a function -- or we're in an error state. That's all easy to handle in this first case.

So let's assume the second case, that we have BoolOrFunc-As-Function. We're going to have to execute it, whether it's "really" BoolOrFunc or SecondFunc, so, at the last moment possible in your code, do so.

This is obviously where things get complicated without more pseudo-code (or production code) from you. If the "possible BoolOrFunc" returns true or false, you know you have a function version of BoolOrFunc. If it returns undefined, you know you just called SecondFunc. Again, depending on what you're trying to do, you branch here. If you have a true or false, you have to process out "BoolOrFunc-as-function", else you've called SecondFunc and are likely done.

That doesn't answer the more generic question of how to tell if a function returns a specific type without calling it, but does solve your use case as presented here.

But this specific more generic case [sic] isn't too difficult, as long as we're dealing with real, constant booleans in the returns. In that case, the regexp route would work -- find all return\s+.*; and make sure what follows each return is true or false. Or just compare total return count is the same as that for return\s+(false|true);. Or something. And hope the javascript in the function is well formed. What a bear already.

If the function can return local variables (return MightBeBoolean; rather than return true;), you're nearly toast -- or if you accept truthiness or falsiness, you're nearly toast again, since undefined is falsey, and you can't tell the difference between BoolOrFunc or SecondFunc (which would always return "falsey"). It's possible, but not nearly worth writing the parser in any reasonable use case I can imagine. And if you're importing a lib to do that, sheesh. Good luck. Seems like overkill to have function that's slightly more defensive in its calling.

I do wonder if either functions passed as arguments are themselves passed arguments from your Obj.func, which would make things even more interesting.

Got more real code? ;^) But ultimately I'm recommending you don't do the "pass any jumble of arguments, as long as they're in order". I think you can likely do the BoolOrFunc/SecondFunc late evaluation. Otherwise, I hate to say it, but code a bit more offensively here.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.