I read this:
http://hackage.haskell.org/trac/ghc/wiki/ViewPatterns
I like the idea, want to use the extension. I however would like to make sure as to one thing: whether the view function is evaluated once for a single matching.
So let's say we have:
{-# LANGUAGE ViewPatterns #-}
...
f (view -> Nothing) = ...
f (view -> Just x) = ...
view :: a -> Maybe b
Now let's say I invoke f a
. Is view
invoked twice or just once for the given argument a
?
EDIT:
I tried to find out whether this is the case and wrote the following:
{-# LANGUAGE ViewPatterns #-}
import System.IO.Unsafe
blah (ble -> Nothing) = 123
blah (ble -> Just x) = x
ble x = unsafePerformIO $ do
putStrLn $ "Inside ble: " ++ show x
return x
main :: IO ()
main = do
putStrLn $ "Main: " ++ show (blah $ Just 234)
Output using GHC:
Inside ble: Just 234
Inside ble: Just 234
Main: 234
Output using GHC (with optimization)
Inside ble: Just 234
Main: 234
Output using GHCi:
Main: Inside ble: Just 234
Inside ble: Just 234
234
Just once:
Efficiency: When the same view function is applied in multiple branches of a function definition or a case expression (e.g., in
size
above), GHC makes an attempt to collect these applications into a single nested case expression, so that the view function is only applied once. Pattern compilation in GHC follows the matrix algorithm described in Chapter 4 of The Implementation of Functional Programming Languages. When the top rows of the first column of a matrix are all view patterns with the "same" expression, these patterns are transformed into a single nested case. This includes, for example, adjacent view patterns that line up in a tuple, as inf ((view -> A, p1), p2) = e1 f ((view -> B, p3), p4) = e2The current notion of when two view pattern expressions are "the same" is very restricted: it is not even full syntactic equality. However, it does include variables, literals, applications, and tuples; e.g., two instances of
view ("hi", "there")
will be collected. However, the current implementation does not compare up to alpha-equivalence, so two instances of(x, view x -> y)
will not be coalesced.
As for your snippet, the problem is that you're not compiling with optimisation; with both ghc -O
and ghc -O2
, the line is only printed once. That's always the first thing to check when you have performance-related problems when using GHC :)
(By the way, Debug.Trace lets you check these kinds of things without having to write manual unsafePerformIO
hacks.)