Avoiding if-else Hell: The Functional Style

21 pointsposted a year ago
by thunderbong

21 Comments

sebstefan

a year ago

>Decision Tables

>Instead of hardcoding the logic inside our function, we can place each if-else block in its own function and put those functions in an array, forming a decision table.

In my experience this is always an unwelcome addition to a codebase and a worse choice than just several lines of ifs

* You've added a level of indirection

* You've written the code in a less straightforward way, your fellow programmer might now need to spend more time understanding what they're looking at and wondering why you wrote it like that instead

* The ifs are still there, it's not inherently simpler just because it's tucked away

* They could've been tucked away in functions, which have the benefit of having names, making them (maybe) a bit more self-explanatory?

But most importantly

* You've introduced an abstraction, and it might be the wrong one

What if one of the conditions need to have a different kind of follow up?

Some people then build two separate decision tables depending on what the follow up needs to be, some take the outstanding "if" out of the decision table and add it back in the regular control flow, which kind of highlights the problem on its own, but...

Some other people double down and make the decision table a more robust abstraction, and in my opinion this is the worst outcome. You end up with some bullshit Predicate & PredicateEngine, callbacks, why not maybe async, and at that point you need to look at it and realize "We're making a framework, and we will eventually need to extend it until it encompasses the entire possibilities of the language's control flow, except with massive overhead, and nothing of value being created"

hansvm

a year ago

All they really did was flatten the if/else to remove a few layers of nesting. It's not a bad idea in the abstract (not suitable everywhere of course), even if you don't like their particular implementation.

  // Original
  if a
    if b
    else
  else
    if b
    else

  // New
  if not a and not b
  if not a and b
  if a and not b
  if a and b

kstenerud

a year ago

    async function assignDriver(rider, availableDrivers) {
        const driverDistances = await calculateDistances(rider.location, availableDrivers);

        for (let driver of availableDrivers) {
            if (driverDistances[driver.id] > 5) {
                continue;
            }
            if (rider.preferredVehicle && rider.preferredVehicle !== driver.vehicle) {
                continue;
            }
            if (driver.rating < 4.0) {
                continue;
            }
            if (driver.rating >= 4.5 && rider.preferences.includes('Premium Driver') && !driver.isPremiumDriver) {
                continue;
            }
            return driver;
        }

        return null;
    }

dahart

a year ago

Personally I think the most valuable concept is simply naming the internal parts of a larger block of logic. The article does that in the last step, but doesn’t name it in the summary. :P Doing the naming at any step with temporary variables or functions usually makes it cleaner and clearer.

exceptione

a year ago

Exactly. Naming the constituting parts also makes it clear if there is a bug.

Example. Is there something wrong in the following statement? You don´t know, because it does not specify what the intended meaning of the statement is.

    if (foo.a <= 3 && bar || bar > 34 && ( ...etc )) 

Take away: break up the parts of you logical condition, give them a name, reassemble the condition from the logically named parts. A a rule of thumb, a condition that reads like a regular sentence is a clear condition.

satisfice

a year ago

Every article like this presents itself as if one programmer’s bugaboo is a problem for all programmers.

Notice that the author didn’t explain how the nested IF ELSE form worked. That’s because we all know how it works. It’s dirt easy. There is a lot to be said for programming in a way that is familiar and plain.

Yes, there are clever alternatives, but most of them require more time to stare at them and work out what is happening.

kbrecordzz

a year ago

Computer programs are sequences of operations that happen IF a condition is true or ELSE something else happens. If you don’t like if-else, you’ve misunderstood the whole thing #stopthehate

cdaringe

a year ago

Absolutely false! It’s a common perspective, but certainly not the only lens to look at it from.

Computer programs are hoards electric voltages changing to discrete levels at fixed intervals to create interesting and coordinated effects on surrounding devices.

Computer programs are 0-N statements of expressions to evaluate.

Theres 2 perspectives completely devoid of “if.”

kbrecordzz

a year ago

I appreciate all perspectives, just remember to not #hate on ones without reason (especially when they correspond closely to what happens on the physical CPU and are both timeless, simple and powerful, like if-else)

cdaringe

a year ago

Gleam lang has no if—only case (aka match) expressions. Its sincerely delightful. Its more capable than ifs by bringing multi conditions into a single expression, allows for closures to funnel into assignments instead of forcing a mutation in the parent scope.

Just lovely. For some reason, rust and ocaml added ifs even though they have match. In rust if-let has orthogonal value, but id happily live without it for less syntax

beretguy

a year ago

What problem did it solve again?

When I have some complex logic to deal with a draw a state diagram and build my if statements based on that.

user

a year ago

[deleted]

jmclnx

a year ago

It needs to be said, "elseif" statement should not exist at all.

I have fixed a lot of bugs due to "elseif" over the decades in various languages. The person who came up with that construct should endure the Confy Chair.

https://www.youtube.com/watch?v=T2ncJ6ciGyM

bayindirh

a year ago

else-if has useful properties, which allows designing flows with simpler condition statements and allows processor to jump less and lose less time (low-power systems are still and will be a thing).

If we didn't have else-if, somebody will certainly lament "If we had something called else-if, I'd not have fixed a lot of bugs due this complex condition statements in various languages. The person who opposes implementation of such a simple construct should endure the Confy Chair".

So, it goes both ways.

orwin

a year ago

I'm not a branchless coding fanatic at all, but arguing for else-if with execution speed arguments isn't the way.

Moreover, else-if is probably the most misused conditional, so while I disagree that it shouldn't exist (it has some uses), GP has a point.

Although linters now are good enough now to catch poorly used conditionals, so his point is way less relevant than it was a decade ago.

bayindirh

a year ago

> but arguing for else-if with execution speed arguments isn't the way.

Depends on the code base and problem domain you code / live in. In most cases you're right, but in my domain it's a different story.

> Moreover, else-if is probably the most misused conditional

You can misuse anything in any language, so by this logic we should argue that we shall abolish all programming altogether. I can misuse threads to make a program slower, misuse memory allocator to fragment the memory, etc. etc.

So I don't think "but people misuse it" is a great argument either.

user

a year ago

[deleted]

al2o3cr

a year ago

Nonsense. This is like arguing that because you've seen plumbing that was installed badly with wrenches, that wrenches shouldn't exist.