Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell multiple statement efficiency

Is this efficient for checking multiple statements in Haskell? Or this there a better way?

case ((x > -10) && (x < 20),x /= 9,(x `mod` 2) == 0,x) of 
    (False,_,_,_) -> error "Not in range"
    (_,False,_,_) -> error "Must not be 9"
    (_,_,False,_) -> error "Must be even"
    (True,True,True,10) -> stuff ()
    (True,True,True,20) -> stuff ()
    _ -> error "Error Message"
like image 681
ArchHaskeller Avatar asked Feb 23 '23 06:02

ArchHaskeller


2 Answers

It's sometimes difficult to come up with small examples of this problem which don't look contrived, but they do happen. Sometimes you need a bunch of computed results to figure out how to split a function into its cases.

So yes, I often find it's cleanest to use case on a tuple of things-I-might-care-about to build complex decision processes. I trust laziness to compute the minimum required to resolve which branch to invoke.

It's worth trying to express your tests via Boolean guards (or even pattern guards), but sometimes there's nothing to beat tabulating the computed values you need in a big tuple and then writing a row for each interesting combination of circumstances.

like image 96
pigworker Avatar answered Mar 05 '23 10:03

pigworker


Assuming that caring about efficiency is really important, and is not premature optimization, you should optimize for the case which is most common; I think that even in Haskell, it means that you want to have the True,True,True cases on top.

Actually, in the given case, if x == 10 or x == 20 you don't need to do the other tests - you don't even need to build thunk computing them; and the compiler cannot know (without profile-guided optimization) which is the code path which will be executed the most, while you should have a reasonable guess (in general you need profiling to verify that).

So what you want is something like the following (untested):

case x of
  10 -> stuff ()
  20 -> stuff ()
  _ -> case ((x > -10) && (x < 20),x /= 9,(x `mod` 2) == 0) of 
    (False,_,_) -> error "Not in range"
    (_,False,_) -> error "Must not be 9"
    (_,_,False) -> error "Must be even"
    _ -> error "Error Message"

Disclaimer: I did not verify what happens to this code and to the original one after all optimizations.

like image 25
Blaisorblade Avatar answered Mar 05 '23 09:03

Blaisorblade