News for hackers. Stuff that matters.
Re:Using ‘switch’ to match arbitrary logic instead of string... by skrebbel on Monday August 19, 2019 @06:15AM (#20736459)
Coded the "normal" way, you get this:

    if(githubUrl) {
        return config.github;
    }
    if(twitterUrl) {
        return config.twitter;
    }
    return {
        to: '',
        ...
    };
IMO this is both clearer and easier on the eyes. Can anyone show me an example of this `switch` pattern that actually makes it better than the obvious if-based version?

I mean, given that they're early returns, you don't need any `else` statements. And in situations where you couldn't use early returns, the `switch` pattern would require `break;` statements. That would make that code uglier to the same extent as adding `else`s above. What am I missing?

Re:Using ‘switch’ to match arbitrary logic instead of string... by tumetab1 on Monday August 19, 2019 @07:04AM (#20736730)
In the end I think it's a option of style in which "if" and "switch" are neither the correct match.

The goal is to define a value (a variable or return value) in which a value is always established and it's established through several checks (exclusive or not).

In this case the goal is to the definitively define a config given a URL.

The "switch" and a default condition which I think express better that a value must be defined. With "ifs" we can add more complex logic and avoid the problematic "breaks" but we loose that in your face "default" condition.

In theory it would be better to have a different control structure like "pickJustOne":

  return pickJustOne(url) {
    githubUrl: config.github;
    twitterUrl: config.twitter;
    default: config.unknown;
  }
"pickJustOne" would have a companion named "pickOneByOrder" that would work like an "if" where the order of evaluation would matter.
Re:Using ‘switch’ to match arbitrary logic instead of string... by Razengan on Monday August 19, 2019 @07:51AM (#20736926)
EDIT: Oops, chalk this up as another case of reinventing the wheel. We already have all this in the form of the ?: ternary operator, as masklinn pointed out:

https://news.ycombinator.com/item?id=20736714

----

Inspired by this, I cooked up a crude imitation in (ugly/functional'ish?) Swift:

    let result = [(firstCheck,  1),
                  (secondCheck, 2),
                  (thirdCheck,  3)]
                  .first { $0.0() }?.1
That iterates over an array of (() - > Bool, Value) tuples and returns the second element of the first tuple whose first element is a function that returns true, otherwise nil. The types of all values must be the same. Could shorten .first to .firstTrue or something.

And a version that's closer to your idea:

    typealias BooleanFunction = () -> Bool
    typealias FunctionValuePair<ResultType> = (BooleanFunction, result: ResultType)

    func pickOne<ResultType> (_ pairs: FunctionValuePair<ResultType>...) -> ResultType? {
        pairs.first { function, result in
            function() == true
        }?.result
    }

    let result = pickOne(
        (firstCheck,  "one"),
        (secondCheck, "two"),
        (thirdCheck,  "three"))
        ?? "default"
You could make the default result an argument for pickOne so that it never returns nil, but I prefer to keep it an optional.

Though it's too early in my day to think of better names. :)

Re:Using ‘switch’ to match arbitrary logic instead of string... by cr0sh on Monday August 19, 2019 @03:11PM (#20740803)
Maybe something like a hash table/array could be used, predefined with the key being the URL and the value being the config, then the function could do something like return the value from the array given the key, with some kind of logic to return the unknown config by default if the key wasn't in the array (this would all depend on the language - it could be that such a method might be worse for readability; it would also depend on if the array could be defined elsewhere as an included file/module or something, etc).
Re:Using ‘switch’ to match arbitrary logic instead of string... by jbverschoor on Monday August 19, 2019 @06:20AM (#20736473)
Yeah never really liked switches because of the "breaks". Switch statements are a more like goto, and are more low level. I tend to do:

  if (xx) {
  } else if (yy) {
  } else {
    // maybe some exception for unhandled value
  }
And to align my ifs:

  if (false) {
  } else if (xx) {
  } else if (yy) {
  } else {
    // maybe some exception for unhandled value
  }

But then team members and tooling start to complain about dead code.
Re:Using ‘switch’ to match arbitrary logic instead of string... by w0utert on Monday August 19, 2019 @06:45AM (#20736633)
This style of writing switch statements as long sequences of if-elseif-else makes it very easy to mess up when you add additional clauses. It also does nothing to guarantee the conditions are mutually exclusive, which would make the comparisons order-dependent. Last but not least I would guess the compiler does not have as many options to optimize this kind of code, compared to switch statements (a switch statement can be compiled to either chained jumps, or a jump table, but the if-elseif-else implies there could be order-dependencies and/or side-effects evaluating the conditions), limiting the compiler.

This is all reasoning from the perspective of a compiled language of course. But even in Javascript implementing switch-statements as if-elseif-else seems like an anti-pattern to me.

>> Switch statements are a more like goto, and are more low level.

I know about 'goto considered harmful', but sometimes a simple jump table is just what you need, no need to come up with 'better' ways ;-)

Re:Using ‘switch’ to match arbitrary logic instead of string... by jbverschoor on Monday August 19, 2019 @06:59AM (#20736703)
If the code produces sideeffects, a switch statement is not even an option, as it can only compare values (at least in this form). If you're checking some status code, I still prefer ifs. The example in this post is simply misuse of a switch statement.

Interestingly, this optimization is relatively new for LLVM:

http://llvm.1065342.n5.nabble.com/llvm-dev-RFC-JumpMaps-swit...

https://reviews.llvm.org/D32742

Re:Using ‘switch’ to match arbitrary logic instead of string... by w0utert on Monday August 19, 2019 @07:55AM (#20736951)
>> If the code produces sideeffects, a switch statement is not even an option, as it can only compare values (at least in this form)

Yes, this is exactly one of the reasons to use switch statements instead of if-elseif-else, were possible.

Even if in the absence of side effects it is still easy to have overlapping conditions in the if-elseif-else, for example misspelling an enumeration value, some copy/paste mistake, etc. A switch statement does not allow this.

>> If you're checking some status code, I still prefer ifs

My philosophy is always to write code that expresses as much as possible its intent, and the sequence of operations the computer has to take. Performing some action based on a status code, in my mind, is like looking a procedure by its label, and executing it. Not looking at all procedures one-by-one until you find one that 'advertises' it applies to your status code. To each their own, I guess ;-)

Re:Using ‘switch’ to match arbitrary logic instead of string... by jbverschoor on Monday August 19, 2019 @08:08AM (#20737015)
>> A switch statement does not allow this. True, but a compiler will detect unreachable code when you do:

  if (x) {
  else if (x) {
    // warning
  }


>> My philosophy is always to write code that expresses as much as possible its intent agreed

>>, and the sequence of operations the computer has to take. From a performance perspective yes, from a logic / maintanance perspective not really.

Re:Using ‘switch’ to match arbitrary logic instead of string... by w0utert on Monday August 19, 2019 @10:55AM (#20738273)
>> True, but a compiler will detect unreachable code when you do

Interestingly, neither clang nor g++ give a warning for this case:

  int main(int argc, char **argv)
  {
    int x = argc;
  
    if (x == 0)
    {
      x = 10;
    }
    else if (x == 1)
    {
      x = 20;
    }
    else if (x == 0)
    {
      x = 30;
    }

    return 0;
  }

  # g++ bla.cpp -o out -Wall
(no warning)

  # clang bla.cpp -o out -Wall
(no warning)

I assume this is because the compiler can never be 100% sure a variable cannot be changed midway through testing each of the conditions because of aliasing, so it won't throw a warning. In this particular example this obviously is not relevant, but I can imagine some pathological example where one of the tests has side effects that change the result of the later tests, and to avoid false positives the compiler simply never warns about unreachable code for these things.

In a sense, this is exactly what I mean by writing code that matches exactly what you want to happen. If I write 'switch (x)', it's pretty clear the test is executed exactly once. If I write a sequence of if-elseif-elseif-else statements, the conditions are evaluated in sequence, which is not what I want.

Re:Using ‘switch’ to match arbitrary logic instead of string... by rhinoceraptor on Monday August 19, 2019 @12:46PM (#20739340)
As others have said, this pattern is used for pattern matching when the language doesn't have specific constructs for it.

When I use it, I will extract it into a separate function, and just return the needed condition. That way you don't need to worry about case fall through.

    function getCondition(param) {
        switch (true) {
            case ConditionOne(param): return 'condition one'
            case ConditionTwo(param): return 'condition two'
            case ConditionThree(param): return 'condition three'
        }
    }
Re:Using ‘switch’ to match arbitrary logic instead of string... by emilsedgh on Monday August 19, 2019 @07:00AM (#20736709)
Early returns are underrated.
Re:Using ‘switch’ to match arbitrary logic instead of string... by skrebbel on Monday August 19, 2019 @07:07AM (#20736739)
They sure are. I often extract logic into a function just so I can use early returns and have less local state.

I mean, they're a recipe for disaster in C++, but I don't code much C++ anymore. In JS and similar-enough languages it's lovely.

Re:Using ‘switch’ to match arbitrary logic instead of string... by Agentlien on Monday August 19, 2019 @07:46AM (#20736899)
What do you mean by "recipe for disaster"? I use them a lot in C++ and as long as you stick with RAII (which you really should), I don't see any language-specific problems with it.
Re:Using ‘switch’ to match arbitrary logic instead of string... by skrebbel on Monday August 19, 2019 @08:42AM (#20737192)
Sure, if all the code you write and call into (OSS etc) is fully RAII then you're good. When I last did C++ for real, lots of code out there didn't meet that standard, not in the least libraries and vendor software, so more experienced programmers than me taught me to be better safe than sorry.
Re:Using ‘switch’ to match arbitrary logic instead of string... by falcrist on Monday August 19, 2019 @07:41AM (#20736876)
I typically code in c and c#, and have been told by older engineers that it's important to avoid early returns because they can be confusing, especially when skimming through the code.

I kind of see what he's talking about. It seems like it's a little easier to reason with the code and easier to set breakpoints sometimes.

All that's to say: I'm curious as to why early returns would be better in JS.

Re:Using ‘switch’ to match arbitrary logic instead of string... by hackinthebochs on Monday August 19, 2019 @12:18PM (#20739102)
>avoid early returns because they can be confusing

It seems to me like the exact opposite is true. Early return conditions stand out in your code and so the structure matches the flow. Its much clearer than having error state weaved through the mainline behavior of the function.

I'm pretty sure the pattern of having only one return is popular among older engineers because it makes it easier to use pre-condition/post-condition/invariant style reasoning which used to be popular. Outside of this scenario I see no reason to avoid early returns.

Re:Using ‘switch’ to match arbitrary logic instead of string... by humanrebar on Monday August 19, 2019 @07:20AM (#20736795)
They're fine in C++ given RAII guarantees. I feel safer using them in C++ than most other languages.
Re:Using ‘switch’ to match arbitrary logic instead of string... by mschuetz on Monday August 19, 2019 @06:50AM (#20736660)
I find if preferable to switch all the time. Switch needs two indents and virtually always that extra break, which makes it more complex all around than a simple if.
Re:Using ‘switch’ to match arbitrary logic instead of string... by metalgearsolid on Monday August 19, 2019 @06:34AM (#20736563)
Swift's nil-coalescing operator is a great solution for this.

  return config.github ?? config.twitter ?? default
Re:Using ‘switch’ to match arbitrary logic instead of string... by lsaferite on Monday August 19, 2019 @08:32AM (#20737130)
And you just introduced a bug.

You refactored the code and changed the logic without noticing.

Re:Using ‘switch’ to match arbitrary logic instead of string... by degif on Monday August 19, 2019 @06:45AM (#20736635)
The same is in JavaScript:

  return (twitterUrl && config.twitter) || (githubUrl && config.github) || default
Re:Using ‘switch’ to match arbitrary logic instead of string... by masklinn on Monday August 19, 2019 @07:01AM (#20736714)
Seems like an unnecessary mess when you could just use ternary expressions:

    return twitterUrl ? config.twitter
         : githubUrl ? config.github
         : default
Re:Using ‘switch’ to match arbitrary logic instead of string... by Y-bar on Monday August 19, 2019 @07:48AM (#20736914)
I envy you, I’d love to grok ternary expressions better but every time I just end up having to spend a long time unrolling the logic.
Re:Using ‘switch’ to match arbitrary logic instead of string... by env123 on Monday August 19, 2019 @07:22AM (#20736800)
I think the `switch` pattern is only applicable when you're checking for a large number of configurations that are of boolean type - ie. isReadOnly, isAdmin, isUser, isSuperUser, hasPaid, isActivated, isBanned, etc - it basically depends on your use case, unusual pattern to use, but it has its merits if applied in the right time by the right developer

case isReadOnly: // change something;

case isAdmin: doAdminStuff();

case hasPaid: updateFinance();

case isActivated: updatedUserInfo();

and so on...

Re:Using ‘switch’ to match arbitrary logic instead of string... by Zenst on Monday August 19, 2019 @07:33AM (#20736844)
Very true, then somebody else ends up maintaining the code and gets a ticket to add this feature and chaos plays out.

One thing I learned early on - true and false vary across platforms. Hence I learned to define true and false at the start of the code with realTrue=(1=1) and realFalse=(1=0). As some systems have 1 or any positive value as true, some have -1 as true. It can get messy, bit like endians (big or little) the same holds true for true and false.

Re:Using ‘switch’ to match arbitrary logic instead of string... by VBprogrammer on Monday August 19, 2019 @08:11AM (#20737029)
> Hence I learned to define true and false at the start of the code with realTrue=(1=1) and realFalse=(1=0)

I'm sure the platforms / language you use make this a sensible choice but I'm not going to lie, if I came across that in someones code I'd be screaming WTF at the top of my lungs.

Re:Using ‘switch’ to match arbitrary logic instead of string... by Zenst on Monday August 19, 2019 @10:49AM (#20738207)
I learned to code in the 70's, predating C99 and having to embrace many diverse platforms (we had more than one mainframe maker back then ;).

But nice explanations here: https://stackoverflow.com/questions/17010041/why-define-true...

TL;DR portability