How to make GHC evaluate static expression only once

I have a trivial example of an SQL-like join for ordered lists: if outer is True , then it is concatenated; otherwise this intersection:

 import System.Environment main = do [arg] <- getArgs let outer = arg == "outer" print $ length $ joinLists outer [1..1000] [1,3..1000] joinLists :: (Ord a, Num a) => Bool -> [a] -> [a] -> [a] joinLists outer xs ys = go xs ys where go [] _ = [] go _ [] = [] go xs@ (x:xs') ys@ (y:ys') = case compare xy of LT -> append x $ go xs' ys GT -> append y $ go xs ys' EQ -> x : go xs' ys' append k = if {-# SCC "isOuter" #-} outer then (k :) else id 

When I look at it, I see that the isOuter condition is evaluated every time append is called:

 stack ghc -- -O2 -prof example.hs && ./example outer +RTS -p && cat example.prof individual inherited COST CENTRE MODULE no. entries %time %alloc %time %alloc MAIN MAIN 44 0 0.0 34.6 0.0 100.0 isOuter Main 88 499 0.0 0.0 0.0 0.0 

But I would like the condition to be evaluated only once, so the append in the go loop is replaced with either (k :) or id . Can I make him somehow? Is this related to memoization?

EDIT: Looks like I misinterpreted the profiler output. I added tracing to the append definition:

 append k = if trace "outer" outer then (k :) else id 

And outer is printed only once.

EDIT2: If I replaced append with a append definition, then the if condition is evaluated only once:

  append = if outer then (:) else flip const 
+7
optimization haskell
source share
1 answer

I would try pushing lambda inside:

 append = if {-# SCC "isOuter" #-} outer then \k -> (k :) else \k -> id 

The source code is essentially \k -> if outer ... , which first takes an argument and later checks the guard. The above code checks the security before accepting the argument.

Alternative:

 append | outer = \k -> (k :) | otherwise = \k -> id 

You can simplify these lambdas for a more readable form.

+5
source share

All Articles