I am just learning Haskell and wrote two programs from a tutorial site, such that
maximumnowhere :: (Ord a) => [a] -> a
maximumnowhere [] = error "empty"
maximumnowhere [x] = x
maximumnowhere (x:xs) = if x > maximumnowhere xs then x else maximumnowhere xs
and
maximumwhere :: (Ord a) => [a] -> a
maximumwhere [] = error "empty"
maximumwhere [x] = x
maximumwhere (x:xs) = if x > maximum' then x else maximum' where maximum' = maximumwhere xs
I thought these two programs are fairly equivalent, cause I thought, the where binding only replaces the variable with its content. but when I run it in ghci, the first one was way slower than the latter, especially for an array with length over 25. Probably, the where binding makes this huge performance difference, but I don't know why. Can anyone explain it for me?
Definition on Haskell Where Function. Haskell where is not a function rather it is a keyword that is used to divide the more complex logic or calculation into smaller parts, which makes the logic or calculation easy to understand and handle.
Haskell (with the GHC compiler) is a lot faster than you'd expect. Used correctly, it can get close-ish to low-level languages. (A favorite thing for Haskellers to do is to try and get within 5% of C (or even beat it, but that means you are using an inefficient C program, since GHC compiles Haskell to C).)
Haskell is a non-strict language, and most implementations use a strategy called laziness to run your program.
Let bindings let you bind to variables anywhere and are expressions themselves, but are very local, so they don't span across guards. Just like any construct in Haskell that is used to bind values to names, let bindings can be used for pattern matching.
No, they are not equivalent. let
and where
introduce sharing, which means that the value is only evaluated once. The compiler will in general not share the result of two identical expressions unless you tell it to, because it cannot in general tell on its own whether the space-time trade-off of doing this is beneficial or not.
Thus, your first program will do two recursive calls per iteration, making it O(2^n), while the second only does one per iteration, i.e. O(n). The difference between these is huge. At n = 25, the first program results in over 33 million recursive calls while the second only does 25.
So the moral of the story is that if you want sharing, you need to ask for it by using let
or where
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With