Question

Is it OK to have a function that returns true/nothing instead of true/false?

My examples use JavaScript, but I'm wondering about the general case, not attached to one specific language.

The subjects of my interest:

  • Is it OK from the practical side?
  • Is it OK from the logical side?

I mean, some languages provide too much freedom and personally I don't like it. I think it is better to follow the way how it works in serious languages like C++ and Java, but I never worked with them.

var a = 1;
var b = 2;

function foo() {
    if (a === b)
        return true;
}

if (!foo())
    alert('error');

versus

function foo() {
    if (a === b)
        return true;
    else
        return false;
}
Was it helpful?

Solution

It depends on what you mean by "OK".

In the languages that I'm familiar with that have the capability to do so, a null/nil/undefined value is falsy. That means that it would be interpreted the same as false when used in a Boolean operation like described in the examples. However, I cannot say for absolute certainty that this will apply to all languages for all time. I also cannot say that everyone reading and working on your code will know this, especially if it's their first foray into a language that permits this behavior.

Personally, I prefer more explicit code. Even if it's slightly more verbose or less idiomatic for your given language, easier readability and maintainability is often a superior choice. Like anything, though, it's a tradeoff that must be decided among the team building the software. Often, decisions like this can be codified in the configuration of linters and other static analysis tools and violations can be detected (and sometimes auto-corrected) during the development process.

OTHER TIPS

In Ruby, nil and false are falsey, and they are the only falsey values. Every other value is truthy, this includes true (obviously), but also values that some other languages might not consider truthy such as 0, 0.0, "", [], {}, and so on.

There is also a naming convention in Ruby that methods whose primary purpose is to answer a Yes/No question are named ending with a question mark.

So, we have the method Integer#odd?, for example, which (probably not surprising) returns true if the receiver is odd and false if the receiver is even, e.g.

1.odd? #=> true
2.odd? #=> false

It is, however, quite common, to convey some extra information using the return value of a purportedly boolean method. Actually, the most extreme example is not a method but the builtin unary prefix defined? operator, which is a boolean operator that (at least in the most widely-used Ruby implementation) never actually returns a boolean!

The Language Specification (section 11.4.3.2 The defined? expression) only guarantees that defined? returns a truthy value or nil, but it does not require that the falsey value be precisely the value false. And many Ruby implementations use that fact to convey additional information to the programmer. For example, in YARV:

defined? foo #=> nil

def foo; end

defined? foo #=> 'method'

foo = 42

defined? foo #=> 'local-variable'

This is information that is useful for debugging purposes, but yet doesn't hurt when you use the defined? operator in a conditional context like

def foo; end unless defined? foo

Returning nil (the value representing the absence of a value) instead of false for methods that ask for the existence of something is especially common.

In general, you are not supposed to do anything other with the return value than treat it as a truthy or falsey value, but you can inspect it for debugging purposes only.

In the Ruby community, this is considered to be completely normal, and there are methods in widely-used third-party libraries, in the standard libraries, and in the core library that do this, and the specification for those methods is often explicitly written in such a way to allow this, e.g. requiring only truthy or falsey values instead of true or false.

Is it OK to have a function that returns true/nothing instead of true/false?

  • No.

Statically Typed Languages

bool isFoo( int a, int b )
{
        if ( a == b ) { return true; }
}
// Evaluated
isFoo(2, 2) // true
isFoo(2, 4) // true

isFoo(2, 4) is undefined behaviour. It will most likely return true because some value in memory is thrown, and as long as it has any value, it is true.


Dynamically Typed Languages

are hard to read because you have to infer from the value, the return type.

var a = 1; // Must infer that 'a' is an integer
var b = 2;

// must infer from the definition, the boolean
function foo() 
{ 
    if (a === b)
        return true;
}

if (!foo())
    alert('error');

Code is read more than it is written. Because of this, you should be explicit whenever possible, and never leave any ambiguity, and never craft functions with multiple return types. Keep things straight forward and decoupled.

There are three parts that must be considered: semantic, mechanic, and dialect.

Lets start with the easiest, mechanic or what works. If your language does not allow you to return nothing, you should return false. If your language does not define false you probably should return nothing, but now we are moving into dialect.

Remember code has two roles to tell the computer what to do and to convey intent to the next programmer. As you communicate with other programmers you will notice that each language develops conventions in how to communicate. There has been many a java programmer who was derided for using java conventions in C and vise versa. So unless there is specific need to communicate something explicit, follow the conventions of your language.

However sometimes you want to say something explicit. This is where the most difficult and subtle point arises. What are the semantic implications of your code. Note that there are entirely different meanings between not responding and "No" when asked if there are any problems, and each answer is appropriate in a specific context.

So think about what you are going to say and say it.

As you're asking about this in general, it's worth considering what typed languages would make of this.

Many would be able to handle it, for instance in Typescript we might write:

function foo(a: any, b: any): boolean | void {
    if (a === b)
        return true;
}

But in C#, what would we return? The following will not compile, because not all code paths return a value:

public static bool Foo(String a, String b) {
    if (a == b) {
        return true;
    }
}

But we could throw an error instead:

public static bool Foo(int a, int b) {
    if (a == b) {
        return true;
    } else {
        throw new Exception();
    }
}

So we can say that in at least some languages, the values returned from a function must be of a consistent type, or the function must throw an error.

In other paradigms, for instance in functional programming, not returning a value is strictly against the rules, though you may return a Maybe, consider this Haskell code:

g :: Int -> Int -> Maybe Bool
g x y
  | x == y    = Just True
  | otherwise = Nothing

Which would be very strange indeed.

Ultimately yes this is a question of preference but I think we can say that the type boolean is less complex than boolean | undefined or Maybe Bool

Lets take an (Oracle) SQL stored function:

CREATE FUNCTION isEqual(
  a IN NUMBER,
  b IN NUMBER
) RETURN BOOLEAN DETERMINISTIC
IS
BEGIN
  IF a = b THEN
    RETURN a = b;
  ELSE
    RETURN NULL;
  END IF;
END;
/

Then we try calling it:

DECLARE
  value BOOLEAN := isEqual( 1, 2 );
BEGIN
  IF value THEN
    DBMS_OUTPUT.PUT_LINE( 'Equal' );
  ELSIF NOT value THEN
    DBMS_OUTPUT.PUT_LINE( 'Not Equal' );
  ELSE
    DBMS_OUTPUT.PUT_LINE( 'Neither Equal nor Not Equal' );
  END IF;
END;
/

What do you think the output is?

It is: Neither Equal nor Not Equal

db<>fiddle here

Some languages there is a difference between FALSE and an unknown NULL/nil/undefined and you cannot rely on your expctations to hold when a BOOLEAN value can have three states: TRUE, FALSE or NULL (undefined) and there is a semantic difference between saying a value is false or that it is undefined and you aren't sure if it is either true or false.

Remember that programming is not just about making the computer do what you want it to do. It's also about explaining to human readers of the code what you want the computer to do. Returning a value on some code paths and not others is confusing. It may look like an oversight to future readers.

On another note, code patterns that look like

if (condition) {
    return true
} else {
    return false
}

should usually be rewritten to

return condition

I think rather than returning nothing, you should return something.

What I meant is, independent of the programming language, your function/method name should define what it does inside. It's for the readability, right? The naming of your function/method gives some hint when somebody looks at it for the first time. If anybody wants to know what it does, he surely has to go through the whole definition but the name should give some summary first.

Now considering this fact, we always expect something from a function/method right? The name of the function/method tells us what it does and we know, "Ok, we are expecting this from this function/method"

So from your example,

function foo() {
    if (a === b)
        return true;
    else
        return false;
}

What we are observing is that the definition tells us that it checks if two variables are equal both in value and in type. So what do we expect?

If we ask somebody are these two things equal? we'll be expecting either Yes! They are equal or No! They are not equal. Meaning either True or False.

So when your fellow team member is reading your codes, s/he will expect the same thing. But if you return nothing, it doesn't match the expectation.

If you return nothing you can pretty much return anything right? So if we're expecting you function returns True of anything then putting it inside an if condition will always result in True and the if block will always get executed. [Unles you return None, nill, null, 0]

So the caller function has to handle so many scenarios. Are we getting a float value/string value/integer value/etc...

So that's unnecessary checking I would say!

That's why I think returning something is good.

In your case return true/false than nothing.

Also a tip, you can clean your code a bit by one line rather than writing a if-else block.

function foo() {
    return a === b;
}

Is it okay?

In the sense of can it work?

Yes.

Depending on the language. In Java for instance you could have a method that returns 'Boolean' but in practice only either returns true or null and never false.

Does it make sense?

It can, but mostly is an indication of bad design.

It could be a representation of a case where either we know that something is true or we don't know the state of something. I.e.Let's say we teleport a drone onto a random planet. If we see a sun we know a sun exists in that solar system. If we do not see a sun, we don't know, it might be we just currently cannot see it. So a method "isThereASun" might be correct to only reply with "true" or null semantically. However, from a software design point of view that seems unnecessary complicated - any code calling cannot know that 'false' is never be the case and needs to treat null and false as separate cases. In all likelihood the logic to apply in both cases would be the same: Not put out the solar panels at the moment. You could make it clear in the documentation that false never happens, but that is risky (and goes against the languages ideals that you use the return value declaration to make possible answers explicit) - if someone changes the method they need to change that part of the documentation AND make sure every method calling it now covers the case. To avoid this additional burden and make the function's behaviour clear from the onset, one could simple rename it to "doWeSeeASun" or "areSunRaysAvailable". Then the answer should always be true or false and the calling code does not need to worry about null values. (well depending on language it might need to deal with it, but a null value would indicate an unforeseen behaviour that can be dealt with at a higher level like other unforeseen behavior).

Another valid approach

You can also come up with your own response type, say, an enum with values "TRUE" and "NO IDEA". That would make the responses clearly defined for such cases where the answer is binary, but not true or false.

Licensed under: CC-BY-SA with attribution
scroll top