loading...
Cover image for Are you using Void correctly?

Are you using Void correctly?

designpuddle profile image Chris Bertrand Originally published at blog.designpuddle.com Updated on ・4 min read

Vb.Net got something right. Its separation of the trusty function and the subroutine makes perfect sense. Unfortunately, that transition has not made its way to any other recent language. Delphi and Pascal also use this idiom, labelling them Procedures and Functions respectively.

All other languages that omit a value from a method call, return a void type. By default, you will create your function and then decide whether to return something.

Let's look at the purpose of a void function or as I like to refer to them, a subroutine and try to comprehend the utter confusion it can bring.

Related image

Is there a correct usage of Void?

Isn't the purpose of a function to return something?

9 times out of 10 a function will return something. Otherwise, it will do something. These two acts should be tangibly different, yet we group them together. This leads to horrific methods that are not pure, do multiple things and mutate global state, all under the watchful eye of Master Void.

Programming has come a long way, yet we stick to this archaic practice that confounds me to this day. Now I'm not against the notion of functions doing something; This is what we want; The issue is the misuse of the void mechanism specifically.

When saving or deleting a record, is it better to return nothing, or should we return the object, boolean or an identifier of the record changed? Is explicitly checking for some value better than wrapping all that code in a try-catch block.

One valid use case is Logging; where information is captured about the application. Another is an entry point like the main function or task runner whose only job is to run a set of functions synchronously.

Why are you using Void?

Let's start with global properties. My ultimate bugbear with void functions is changing global state. It happens far too often. Don't do it!

The key design decision should be whether the function returns something? When creating a function ask yourself:

  • Does it throw an exception if there is an error?
  • Does it alter an object or some properties?
  • Should we wrap code in a try-catch block?
  • What happens when the function succeeds?

Do we care? Is more information better or worse?

That's down to you, I guess what I'm trying to say is that using void functions can cause confusion. It requires the developer to dig deeper into code because externally the function doesn't disclose any information about its effects.

I believe the only valid use case of a void function is a fire and forget call. So long as it didn't error, you don't care what it did.

Me, hater of the misused void.

A void call shouldn't stop the rest of the code running successfully. It's not required for the rest of the function to do its job.

Are you sure it doesn't do something you want to know about?

This is the first question I as myself when looking at a void function.

It should be yours too!

My aim is not to stop people from using void functions. They are an intrinsic part of how software is developed and consumed, I just urge you to use them when necessary. You wouldn't save a number into a string field.

Don't use void without first thinking about the consequences of doing so.

Roll Safe Think About It Meme | THINK... IS THIS REALLY A VOID FUNCTION? | image tagged in memes,roll safe think about it | made w/ Imgflip meme maker

I'd love to hear your views on this, counter arguments are most welcome!

Discussion

pic
Editor guide
Collapse
aleksikauppila profile image
Aleksi Kauppila

Void is perfectly good return type. In OO your methods either return state or change state.

Those that return state, don't alter the state, and will have some reasonable return type.Those that change state, don't return anything.

Tbh i'm not a fan of the description of "doing something" presented in this post. Changing the state of object describes this better in my opinion.

Martin Fowler has a post about this subject.

Edit: Failures to do that "void" operation is communicated through exception.

Collapse
designpuddle profile image
Chris Bertrand Author

The CQRS pattern has been around for a while and is gaining a lot of traction in our new microservices, event sourcing, multi tenancy environments. It's definitely a valid use case. Equally it adds a whole level of complexity.

Collapse
aleksikauppila profile image
Aleksi Kauppila

Many seem to mix CQRS (the architectural pattern) to this lot simpler practice that Martin describes. Check the post again 😊

Thread Thread
designpuddle profile image
Chris Bertrand Author

They're very much related though. Interestingly if you look at what he says, He in fact determines that changing state and returning values should be deemed as two separate operations.

"It would be nice if the language itself would support this notion. I could imagine a language that would detect state changing methods, or at least allow the programmer to mark them. "

This one could argue is Martin himself notioning that using a void function should be called a "command", and a standard function that returns an object is called a "query". I'd go along with that, and a way of the compiler enforcing that rule would be even better!

"The really valuable idea in this principle is that it's extremely handy if you can clearly separate methods that change state from those that don't. This is because you can use queries in many situations with much more confidence, introducing them anywhere, changing their order. You have to be more careful with modifiers."

This is where the problem lies, your return type functions can still change global state, and this is where I feel we are not enforcing enough rules around what a function "should/is allowed" to do.

Thanks for sharing your views, I appreciate the healthy debate!

Collapse
somedood profile image
Basti Ortiz (Some Dood)

I think this is a very interesting take on something I never really thought about. I consider myself to be quite strict about the semantics of code, so this comes as a surprise that this has never crossed my mind.

If I may ask, what may be the other valid use cases for void calls besides logging?

Collapse
designpuddle profile image
Chris Bertrand Author

Thanks, It drives me up the wall.

There really aren't many valid use cases. Any IO operations which may include logging.

Then any entry points to an app. Eg the main() func, setting global config, automapper, DI or task runners such as Grunt, Gulp etc and Test Frameworks. In principle only anything super high level, and things that could affect the global state.

Collapse
somedood profile image
Basti Ortiz (Some Dood)

By "entry points to an app", am I correct to assume that these include event listeners?

Also, just a tiny nitpick: doesn't the main function call ideally return an integer denoting whether or not the program was successful?

Thread Thread
designpuddle profile image
Chris Bertrand Author

Yes that would be right! The main function call can be either, and it's down to the user to decide which to use. I would recommend it returning an int, but then I think you would have guessed that already 😊

Collapse
zyzmoz profile image
Daniel Cunha (he/him)

I got your point. I do agree that we (developers) should think exactly what the function will perform to assign the right return. Although, I also think it's a kind of extremism say that void isn't recommended at all! I came from a Java background, so there are principles I learned that justify the use of void functions like simple setters! Mostly if we don't get the right idea of some structure or practice, it lead us to a misconception of it!

Btw, well-elaborated article!

Collapse
designpuddle profile image
Chris Bertrand Author

Setters are also valid. I'd lump that with global config/state. The exceptions to the rule (there are no rules) 😅are minimal and the ability to misuse void are much higher in my experience. Thanks for adding to the discussion.

Collapse
designpuddle profile image
Chris Bertrand Author

I think functional programming follows this practice to an extent so that connection makes sense. I really think they should be labelled differently, they do different things. What that label would be though would be harder to reach a consensus on.

Collapse
aleksikauppila profile image
Aleksi Kauppila

Wow, nice touch attacking some junk i wrote few years ago while trying to learn some basic stuff 😂👍

Thanks for taking part to the discussion. Internet’s so cool.

Collapse
theskaf profile image
Θεόδωρος Σκαφιδάς ♫

Delphi is still alive you know. Like procedures and functions?

Collapse
designpuddle profile image
Chris Bertrand Author

And Pascal! And Yes, they separate this concern too!

I love this description from the DelphiBasics site

"Delphi provides 2 types of subroutine - Procedures and Functions. Functions are the same as procedures except that they return a value in addition to executing statements. A Function, as its name suggests, is like a little program that calculates something, returning the value to the caller. On the other hand, a procedure is like a little routine that performs something, and then just finishes."

I'll update the post, thanks for the reminder!

Collapse
theskaf profile image
Θεόδωρος Σκαφιδάς ♫

You are welcome. Good read btw :)

Collapse
smexay profile image
norman

why would you like to advoid me?

Collapse
designpuddle profile image
Chris Bertrand Author

Haha, how could anyone avoid that charm!