4

For learning Haskell (nice language) I'm triying problems from Spoj.

I have a table with 19000 elements all known at compile-time. How can I make the table strict with 'seq'? Here a (strong) simplified example from my code.

import qualified Data.Map as M

-- table = M.fromList . zip "a..z" $ [1..]  --Upps, incorrect. sorry
table = M.fromList . zip ['a'..'z'] $ [1..]
hammar
  • 138,522
  • 17
  • 304
  • 385
Jogusa
  • 5,530
  • 5
  • 24
  • 23
  • Do you know that and why you need it to be strict? If not, I'd say YAGNI. –  May 14 '11 at 10:05
  • This are only 4 elements, as far as I can see? – Ingo May 14 '11 at 10:06
  • 1
    What exactly is that you want to be strict? The elements stored in the map? The construction of the map itself? And why? – augustss May 14 '11 at 11:54
  • By strict do you mean that you'd like the table to be evaluated (created) at compile time? – tibbe May 14 '11 at 12:00
  • @tibbe: Yes! When I test my code in GHCI, the first time I access the table after loading it takes 0.49 sec, later only 0.03 sec. I thought I needed strictness. Or is it different with compiled code? Thank you all for the help. – Jogusa May 14 '11 at 13:21
  • 5
    @Jogusa I think it's going to be very difficult to convince ghc to build the table at compile time. If you absolutely must, then Template Haskell could do it, but not using Data.Map. But why is it bad with a little extra time on the first access? – augustss May 14 '11 at 15:25
  • @augustss: In Spoj the problems have time limit and sometimes it is to tight to pass. This was the reason. But my worry was probably to early, maybe it pass anyway. I have already a good algorithm IMO but I have to polish it. Thank you and a nice weekend! – Jogusa May 14 '11 at 17:10
  • If time is of the essence, I recommend using the [unordered-containers](http://hackage.haskell.org/package/unordered-containers) package. Use the `Data.HashMap.Strict` module. – tibbe May 16 '11 at 06:21

2 Answers2

5

I think you're looking for deepseq in Control.DeepSeq which is used for forcing full evaluation of data structures.

Its type signature is deepseq :: NFData a => a -> b -> b, and it works by fully evaluating its first argument before returning the second.

table = t `deepseq` t
  where t = M.fromList . zip ['a'..'z'] $ [1..]

Note that there is still some laziness left here. table won't get evaluated until you try to use it, but at that point the entire map will be evaluated.

Note that, as luqui pointed out, Data.Map is already strict in its keys, so doing this only makes sense if you want it to be strict in its values as well.

hammar
  • 138,522
  • 17
  • 304
  • 385
  • Data.Map is strict in its keys, so this does nothing whatsoever. – luqui May 14 '11 at 16:48
  • @luqui: Yes, but it's not strict in its values. This is. – hammar May 14 '11 at 16:50
  • Which does nothing in this case. Hmm... or maybe not. It would have no effect if the value list were finite, even `[1..10^10^100]` (because we have to compare with the right endpoint to see if the list should end, thus forcing each value). But since it is infinite, forcing might end up actually doing some computation. Hoo... subtle stuff. – luqui May 14 '11 at 16:58
  • @luqui: I'm assuming the OP is storing something more interesting in his values. Otherwise, there would be no reason for doing any of this. – hammar May 14 '11 at 17:01
  • Yeah that makes sense. I was having a metacognition failure. – luqui May 14 '11 at 20:38
3

The general answer is, you write some code that must force evaluation of the whole datastructure. For example, if you have a list:

 strictList xs = if all p xs then xs else []
      where p x = x `seq` True

I am sure there is already some type class that would apply such forcing recursively and instances for standard data types.

Ingo
  • 36,037
  • 5
  • 53
  • 100