β

Enums > Booleans (?)

fn formatFloat1(f: real, isScientific: bool): str {
    if isScientific {
        return sprintf("%e", f)
    }

    return sprintf("%f", f)
}

fn formatFloat2(f: real, fmt: enum { fixed; scientific }): str {
    if fmt == .scientific {
        return sprintf("%e", f)
    }

    return sprintf("%f", f)
}

fn main() {
    printf("%s %s\n", formatFloat1(1000000, true), formatFloat1(1000000, false))
    printf("%s %s\n", formatFloat2(1000000, .scientific), formatFloat2(1000000, .fixed))
}

Compare the two functions above - formatFloat1 and formatFloat2. The second one uses Enums, while the first one uses Booleans. The first one is not obvious - you have to know that the Boolean parameter is true for scientific notation. And while you're going to look the function up while writing, most of the time is spent reading code, and having to remember that the Boolean parameter to formatFloat1 signifies whether to use scientific or fixed notation is going to greatly increase the cognitive load as the function name doesn't state what the Boolean parameter is supposed to be.

But even more, these Boolean switches often are not helpful by themselves if you just provide a declaration of the function:

fn formatFloat1(f: real, isScientific: bool): str
fn formatFloat2(f: real, fmt: enum { fixed; scientific }): str

Now go ahead and guess what happens if isScientific is false? What will it print? Given just the function name and declaration, it's impossible to tell nor it's always fair to assume that it will fall back to a fixed point representation, what if the fallback is actually truncating the decimal places? It's always possible to find out by simply testing, however that wastes precious development time that we all want to save. And who says you're not going to forget it later [because i always do...]. The Enum version however, is immediately clear with which options it provides.

But to be honest, I wouldn't write this entire thing if Umka didn't have Enum elision. In Umka, almost in all cases the Enum type can be completely elided, so instead of typing out module::EnumType.entry you can just do .entry.

This issue must be close to the heart of C++ developers, as enum class only works as a namespace [and better type checking but that's beside the point], you still have to specify the fully qualified name, which makes it look bonkers, even if it's better than a Boolean - formatFloat2(1000000, module::FormatFloat2::scientific).

Booleans are often misused, and in a lot of cases they can introduce inefficiency since they are used to diverge control flow into a strict dichotomy, where often times it's not applicable.

And I am guilty of misusing Booleans as well - just look at the burning garbage fire SAVESCUM is. Fair to say, a big chunk of this project was built on "eh it's fine" idea, which later piled up. So maybe this post is more for myself than anyone else xP

P.S. Provocative title, I know, very sorry for that :P

-*-