The point is to parse the input into a structure which always upholds the predicates you care about so you don't end up continuously defensively programming in ifs and asserts.
The point is to parse the input into a structure which always upholds the predicates you care about so you don't end up continuously defensively programming in ifs and asserts.
if(!needToDoTheThing()) return;
DoTheThing();
We could have written it this way: if(needToDoTheThing()) {
DoTheThing();
}
else {
return;
}
The later is closer to how pattern match looks like. But in my experience, the majority of programmers prefer early return. I regularly see people "refactor" if-else to if-early-return, but I've never seen the opposite. def TRUE(a, b):
return a
def FALSE(a, b):
return b
def IF(cond, a, b):
return cond(a, b)
assert IF(TRUE, 1, 2) == 1
assert IF(FALSE, 1, 2) == 2
This gives you the conditional statement in most languages ("cond ? a : b" or "a if cond else b").I had to work with this for a while because I wanted to implement Hello World in Javascript. https://github.com/quchen/lambda-ski/blob/master/helloworld/...
## type Silly = Foo | Bar Int | Qux String Silly
## Constructors
def Foo(onFoo, onBar, onQux):
return onFoo()
def Bar(arg0):
return lambda onFoo, onBar, onQux: onBar(arg0)
def Qux(arg0, arg1):
return lambda onFoo, onBar, onQux: onQux(arg0, arg1)
## Values of Silly type are Foo, Bar(x) and Qux(x, y)
## Destructor
def match_Silly(silly, onFoo, onBar, onQux):
return silly(onFoo, onBar, onQux)
You can make a whole language on top of that if you don't mind effectively disabling your CPU's branch predictor.I'd summarise boolean blindness as: implicit (often unsafe) coupling/dependencies of method results; which could instead be explicit data dependencies. That article's example is 'plus x y = if x=Z then y else S(plus (pred x) y)', which uses an unsafe 'pred' call that crashes when x is 'Z'. It avoids the crash by branching on an 'x=Z' comparison. The alternative is to pattern-match on x, to get 'Z' or 'S x2'; hence avoiding the need for 'pred'.
Another alternative is to have 'pred' return 'Maybe Nat'; although that's less useful when we have more constructors and more data (e.g. the 'NonEmptyList' in this "parse, don't validate" article!)
If you nest if/else, you'll quickly approach a point where you have to keep a complex logic tree in your head to determine which states the system could be in inside of any given branch. If you use guard clauses and return early, you'll keep this complexity down to a minimum, since the list of possible states changes linearly with your code instead of exponentially.
I know not everybody likes it, but I think this makes cyclomatic complexity an extremely valuable metric for measuring "ease-of-reading".
const published = posts.filter(post => !post.draft);
safeDiv :: (Monad m, Alternative m) => Int -> Int -> m Int
safeDiv x y = do
guard (y /= 0)
pure (x `div` y)
main :: IO ()
main = do
print $ safeDiv @Maybe 1 0
print $ safeDiv @[] 1 0
-- print =<< safeDiv @IO 1 0 -- guard throws an error in IO
Try it out at https://play.haskell.org/saved/a6VsE3uQ