13

I have a constant declaration in Haskell -- can I force this to be evaluated ahead of time? I'm seeing some code that looks roughly like,

myList = [(a, b), (c, d)]
...
map (f . fst) myList

take time in the fst call when I profile it (it does have 168M calls). The binary representation of myList is quite small, and could be, for example, copied into global memory [if this were a C program]. I'm compiling with -O3 -optc-O3 of course.

Thanks very much!

Generating Lift instances for a custom type

Any expression given to the lift call in sclv's answer must be an instance of Lift. There's a library named th-lift which will generate Lift instances for custom data types. See that package's documentation.

gatoatigrado
  • 16,580
  • 18
  • 81
  • 143
  • I notice that when I write something like "!myList = ..." it will say "Top-level bang-pattern bindings aren't allowed" ... maybe this is forbidden by design? – gatoatigrado May 25 '11 at 09:35
  • You can (probably) do this using Template Haskell. It will be inconvenient. Hopefully there is an easier way. – dave4420 May 25 '11 at 09:55
  • 1
    A top level bang pattern, if it worked, would only force the top level cons of the list. – augustss May 25 '11 at 10:07
  • 1
    Is it the expression `map (f . fst) myList` which you want evaluated at compile time? – John L May 25 '11 at 10:29
  • 1
    @augustss: if a top-level bang pattern did work, then you could use a deepSeq to ensure that it really did force the entire list. – Ganesh Sittampalam May 25 '11 at 10:34
  • 1
    @Ganesh Absolutely. Given that this seems to be a common request, perhaps it should be turned into a ghc feature request. Of course, having the compiler perform arbitrary computations could take a long time. – augustss May 25 '11 at 10:37
  • 1
    You'd also have to decide whether to evaluate top-level bang patterns at compile-time, or eagerly at application startup. I'd favour the latter, with a pragma for the former. – Ganesh Sittampalam May 25 '11 at 11:13
  • 3
    Would you be happy to speculatively evaluate it ahead of time using `par` ? – Don Stewart May 25 '11 at 17:49

1 Answers1

15

Generating a compile-time constant using Template Haskell:

{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH.Syntax(Lift(..))

test = $(lift $ (map (*20) [0..100] :: [Int]))

lift takes a Haskell value and lifts it into a TH Exp. The $() runs the enclosed quote, and splices the generated exp into the code at compile time.

sclv
  • 38,665
  • 7
  • 99
  • 204
  • Thanks very much, this looks right. I'll have to learn some more tempalte haskell before I can verify it works on my real-world example, but this sounds like the right approach. (I'm currently getting this "GHC stage restriction: ... is used in a top-level splice or annotation, and must be imported, not defined locally" message.) – gatoatigrado May 25 '11 at 23:52
  • 2
    @gatoatigrado: Right. Any of the values you use inside the splice have to be defined in another module. After all, they're run before the module is fully compiled :-) – sclv May 26 '11 at 00:25
  • Okay, I got it to work... unfortunately, it doesn't help the performance scene though. I think Don's comment was hinting that the expense of computing the constant was all that mattered, not that its access would become faster when compiled. – gatoatigrado Jun 13 '11 at 04:00