ViewPatterns and multiple calls in Haskell
Asked Answered
F

1

12

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
Fushih answered 20/1, 2012 at 20:12 Comment(1)
GHC has a special hack to avoid recomputation of identical view expressions.Goodard
E
14

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 in

f ((view -> A, p1), p2) = e1
f ((view -> B, p3), p4) = e2

The 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.

The GHC manual

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.)

Eleonoraeleonore answered 20/1, 2012 at 20:15 Comment(4)
Is it possible that GHC is inserting additional spare evaluations because of performUnsafeIO? Don't worry, I used it just to test the feature.Fushih
Yes, that's me being a newbie still. Thanks a lot!Fushih
For some reason I feel like using Debug.Trace is just fine, but unsafePerformIO is generally horrific, even though Debug.Trace just uses unsafePerformIO under the hood.Ceolaceorl
@DanBurton Yes, you're right, I should have used Debug.Trace for that, it would be clearer what I wanted to do.Fushih

© 2022 - 2024 — McMap. All rights reserved.