Void being `()` or `Unit` as some other languages call it allows for some nice 
things. Scala in particular has erased values and whatnot that take advantage 
of this. Some use cases in Nim would be like so:

Nim currently has a feature where `let _ = foo` corresponds to `discard foo`. 
This is because `_` is a special identifier. This behaviour is arbitrary and 
doesn't do things you would expect, such as [this 
one](https://github.com/nim-lang/Nim/issues/13443) in proc definitions, and a 
bug that I couldn't find related to object variants (also to note, you can name 
an object field `_` and it works like a regular parameter but errors if you 
name it `_name`).

There is a section in the [experimental 
manual](https://nim-lang.org/docs/manual_experimental.html#void-type) about the 
void type, and how it can be used in object fields and proc parameters to 
signify a parameter that doesn't exist. This implementation seems a bit verbose 
however, such as the following code:
    
    
    proc callProc[T](p: proc (x: T), x: T) =
      when T is void:
        p()
      else:
        p(x)
    
    proc intProc(x: int) = discard
    proc emptyProc() = discard
    
    callProc[int](intProc, 12)
    callProc[void](emptyProc)
    
    
    Run

Defining `type void = tuple[]` and adding special behaviour to `tuple[]` 
specifically (such as `tuple[]` is a subtype of every type, and it is eluded in 
proc parameters) seems like it would fit Nim's philosophy of being elegant more 
than the existing `void`. `let _ = foo` could be replaced by `let () = foo` a 
la OCaml, and it would fit more of `discard`'s philosophy of "turning into a 
void value".

Not to mention, in the above code snippet, typing `p(x)` would be equivalent to 
`p(())`, which seems more appropriate to translate to `p()` than it having the 
unique type `void`.

It would also mean a standalone `discard` could just be translated to the 
expression `()`. This is just a personal thing, but this would look way better 
in case object branches in my opinion, more appropriate than `discard` or `nil` 
since the intended message is "there are no fields here".

`distinct void`/`distinct tuple[]` types could be used in generics to fulfill 
the effect of special types. This would replace the current convention of just 
using an empty `object` type, which is unpreferable for many reasons that I 
don't think I need to go into in this post. This is even in the Nim standard 
library already, see [StaticParam[T] in 
typetraits](https://github.com/nim-lang/Nim/blob/14b2354b7da36041ca046e60e833b5be9a04f1e4/lib/pure/typetraits.nim#L89).

While writing this post, I remembered an RFC I wrote a while ago, proposing 
`void converters <https://github.com/nim-lang/RFCs/issues/61>`_. The "discard 
overload" part of this RFC is incompatible with what I am suggesting here, 
since `discard` would be pretty much just syntax sugar. I also don't think void 
converters are good since they're hidden control flow, but that's unrelated. 
The point I am making is `tuple[]` will allow for solutions to more abstract 
problems like the ones implied in my RFC if you can do things like `distinct 
tuple[]`. For example, a function that normally returns void but has the 
possibility of setting a global `error` variable could cause problems if the 
code it's called in doesn't check for that `error` variable. If you give it a 
return type `CanError = distinct void`, the compiler will complain if you don't 
discard the call, thus you can be reminded to check for an error.

The problem with this concept is `tuple[]` is currently treated as a value in 
Nim. `let x = ()` works, `echo(())` prints `()` meaning `$() == "()"`, so on. 
So this would be a breaking change. Not to say that anyone is actually using 
`tuple[]` in their code, most people probably don't even account for its 
existence in generics causing some potential bugs, it would just change the 
behaviour of the language. It's also going to harm Nim's maturity when it gets 
implemented, so it might be best if this was considered around Nim 2.0, but 
there are existing features in the language that are incomplete yet related 
such as `let _ = foo`, so I believe it's also fairly relevant now.

Will this happen? Are there any pitfalls that I haven't considered? Does it 
warrant an RFC or is there already one I don't know?

Reply via email to