Hacker News new | ask | show | jobs
by zanecodes 2080 days ago
In Haskell (I know, I know) I would probably write something like this, if I were porting the example one-to-one:

  getInsuranceAmount status = case
      [hasInsurance,  isTotaled,  isDented,   isBigDent   ] <*> [status] of
      [False,         _,          _,          _           ] ->        1
      [True,          True,       _,          _           ] ->    10000
      [True,          False,      False,      _           ] ->        0
      [True,          False,      True,       True        ] ->      270
      [True,          False,      True,       False       ] ->      160
Personally, I'm not a fan of the early return pattern, since it forces me to read the entire function top to bottom (and possibly simulate a state machine in my head) to understand under what circumstances it returns each possible value, and I'm far too lazy for that. In contrast, my example makes it very straightforward to see what set of conditions leads to which value, and vice versa.

Tangentially, this probably wouldn't be a very idiomatic piece of code to write in Haskell, since some of the conditions are likely to be mutually exclusive (what does it mean when isDented is False but isBigDent is True?) My instinct would be to model the status as a sum type, and then to pattern match on its inhabitants instead of writing helpers for 'hasInsurance,' 'isTotaled,' etc, but someone with proper professional Haskell experience might have an even slicker way to think of it.

1 comments

I don't necessarily think what you don't like is related to early returns because the code in the article could be written like

  if (!status.hasInsurance()){
    return 1;
  }
  if (status.hasInsurance() && status.isTotaled()){
    return 10000;
  }
  // ...
to be explicit about the conditions. Or without early returns but with still the same issue:

  if (!status.hasInsurance()) {
    amount = 1;
  } else if (status.isTotaled()) {
    amount = 10000;
  }
  // ... return amount