4

I have a disagreement with one of my colleagues on whether or not functions should have inverse functions available.

I would like to know when/if inverse functions should be used.

For example, say we have a function isTrue(boolean condition){return condition == true;} then what I would do is also include a isFalse(boolean condition){return !isTrue()';} function, given that I know that !isTrue() is used in the code-base.

I argue that this makes the code more readable and less error-prone as you may forget to add the "!" (which has happened to me on occasion). He argues that the function is superfluous as !isTrue(condition); is functionally equivalent to isFalse(condition) and therefore isFalse() should not be included in the API of the class.

Clearly he is right about them being functionally equivalent but I disagree that this means you should not include the isFalse() method. I believe that I have read somewhere that my version decreases cognitive load but I can no longer find the source. Either way, my take is that "This condition is false" is easier to understand than "This condition is the inverse of true".

If you know of any sources on either my take or his and you can share them that would be appreciated.

4
  • 1
    The only time I've ever done this is when I want to bind the opposite in a WPF form or user control, and I can't be bothered writing a ValueConverter. Mar 22, 2021 at 18:52
  • 1
    These examples from the CLR sort of fit: IsClass vs. IsValueType, IsPublic vs. IsNotPublic. Although I think there are some subtleties that make these "opposites" inexact (which is probably why both methods were needed in each case).
    – John Wu
    Mar 22, 2021 at 19:12
  • The issue with this question is, it expects us to guess how isTrue and isFalse could be really named in your code (probably because of the wrong assumption it does not matter). Sorry, but you should really try to give a few more realistic example, this is too contrived.
    – Doc Brown
    Mar 23, 2021 at 22:52
  • @DocBrown I would have done so. However, at the time of writing I was still formulating my own thoughts and I did not want to include the actual case.
    – Byebye
    Mar 24, 2021 at 13:59

5 Answers 5

6

Don’t not avoid negatives. Sure the cpu doesn’t mind them but humans suck at parsing them.

However, the idea that every function should automatically have some complementary function is silly over design if you can’t point to a real need.

Also, !isTrue() is not the inverse function of isTrue(). It’s the logical compliment. An inverse function swaps the input and output. A logical compliment negates the output.

Problem is the name isFalse is also a negative. It doesn’t add any meaning. You can avoid a ! by picking the right name. Say, isOccupied() vs !isVacant(). But that only works when the name has a different meaning on it’s own. It doesn’t help if the name just gives you a negated meaning. It’s far better to use a name that has the correct meaning without needing a negative word attached to it.

So yes, sometimes a good name can avoid a nasty ! but not all the time. It’s best to talk to your team and find out if this really helps them or not.

2
  • I wouldn't add a inverse function if the naming is equally cumbersome as adding a "!". Having a grammatical complement to the function name as in your example is one of my requirements. Your example is actually very similar to my exact case. I'm marking your answer as "the answer" because it envelopes my exact reasoning and the example is similar to my case. I would like to add that @Alexei Levenkov 's answer adds the consideration of API exposure which I found very useful.
    – Byebye
    Mar 23, 2021 at 9:27
  • 1
    "Problem is the name isFalse is also a negative." In the context of the "don't use negatives in naming" style guide, which is what I think you're intending to refer to here, isFalse is considered positive. isNotTrue would be negative, as it would lead to thing like !isNotTrue. There's always subjective semantical edge cases to this, but the general idea is to not push semantical negations in naming, as negation should be left to the negation operator, as it's easier to parse one consistent negation (!) than it is to parse several (!, "not", "un-", "in-", ...)
    – Flater
    Mar 23, 2021 at 11:26
6
bool IsLate()
{
  return (Now > DueDate);
}

What's the complement method here? IsEarly? IsNotLate? What if DueDate is undefined or null (depending on the language)? Are you going with this implementation?

bool IsEarly()
{
  return !IsLate();
}

Well no, because you can be not late and not early. You can be right on time. What about this?

bool IsNotLate()
{
  return (Now <= DueDate);
}

Okay, you've got two methods to maintain now. Does that make sense in your problem space? Is it worth having two things to fix if you need to change the syntax or interpretation of DueDate someday?

Bottom line: Write the code that does what you mean. If what you mean is "that idea of lateness, do something when it's not happening," that's probably !IsLate(). If what you mean is "that idea of being on time or early, do something in that event," that's probably a separate IsNotLate() or IsEarlyOrOnTime() method. And if the straightforward way of expressing one of those ideas is directly in terms of the complement, okay, do that.

Let the code map to the problem space.

3
  • 1
    It's IsInTime, maybe
    – RiaD
    Mar 22, 2021 at 22:23
  • In my case the space is binary. There is no wriggle room. If my situation would be like your example then I would indeed not do this and go with a function that is more expressive. In any case thank you for the reply, much appreciated.
    – Byebye
    Mar 23, 2021 at 8:52
  • While you're right that there isn't always a binary state (i.e late vs ???), OP's question seems to be restricted to specifically binary states where there are only two complementary options.
    – Flater
    Mar 23, 2021 at 11:29
2

As in many cases, this is context dependent. IsTrue/IsFalse are contrived examples, but think of real life cases. A collection might have a property IsEmpty. It makes sense to ask the question if (myStuff.IsEmpty) {...}. Does it make sense to have an IsNotEmpty property? Is it any more legible than !myStuff.IsEmpty?

I've written such inverse properties before, in contexts where it made sense. In WPF, where you have UI elements whose properties are declaratively bound to object properties, there is value in having it so you can bind <ListView DataContext="{Binding MyStuff}" Visibility="{Binding IsNotEmpty}"> to show a listview only if there is data in the list. But that's a very specific use case, meant to work around limitations in the framework.

It might make sense in some cases, or feel overly verbose and unnecessary in others. Consult your teammates and code reviewers, reach a consensus and stick to it.

2
  • We're a team of three where one did not have an opinion and I disagreed with the other during a code-review if my code. Personally I have a propensity to wrap boolean conditions in methods especially if they're long. Hence this question, as I would like to know if I'm on the extreme side of the spectrum when I make decisions like this. Thank you for the reply.
    – Byebye
    Mar 23, 2021 at 9:13
  • I like to wrap Boolean conditions myself. But I temper that with an insistence on a good name. Without that I’d rather break it up with white space. Mar 23, 2021 at 9:59
1

Programming is about expressing ideas and there may be more than one way to express the same idea.

In binary logic, !isTrue(x) and isFalse(x) seem to be equivalent and in the end, it seems to be a matter of taste: it’ll be difficult to prove objectively that one is better that the other.

Nevertheless, !isTrue(x) and isFalse(x) do not have exactly the same semantic: fuzzy logic or other form of probabilistic logic allows things to be “not true” without necessarily being false: there are many shades of grey between black and white, and this is why it can make sense to express the primary intent.

Moreover, it’s not often as clear cut as isTrue() and isFalse(), even with binary logic. Business logic is rich in such semantics subtleties where the contrary of isSmall() is not isBig() but isBig() or isMediumSize() or isUnknown().

1
  • 1
    I agree. In my case the space is binary and not [0.0, ..., 1.0] as in most probabilistic logic. If the space was not binary I would indeed follow your advice.
    – Byebye
    Mar 23, 2021 at 8:54
0

Only place where I actually saw this in regular code was comparison operator overloading in C++ before C++20.

Another quite niche use case comes when working in embedded. Many microcontrollers will have separate registers allowing fast setting of a GPIO to high or low state. Those are often exposed as separate functions to allow for direct without using an if if it isn't necessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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