DantheMan
DantheMan

Reputation: 7417

Haskell how to force evaluation of Data.Map in haskell?

So I have a Map that is Strict

import qualified Data.Map.Strict      as Map
import qualified Data.ByteString       as BS
import qualified Data.ByteString.Char8 as C

type Key = Int
type Value = BS.ByteString

data KeyValue = KeyValue !(Map.Map Key Value)

source :: [(Key, Value)]
source = zip [0..] $ map (\x -> BS.concat $ replicate 10000 $ "alsfdd" `C.append` (C.pack $ show x)) [0..10000]

I am trying to get the Map to evaluate each time I put an element...

 putStrLn "Putting 10000 Strict ByteStrings into a Map"
 let newMap = foldr (\(k,v) i -> Map.insert k v $! i) Map.empty source
 putStrLn "Done..."
 putStrLn "Launching interactive mode"
 forever $ do
      putStrLn "Enter an integer:"
      k <- getLine
      print $ Map.lookup (read k) newMap

However, the Map doesn't evaluate until I get into "interactive mode" and submit a query.

The only way I can get it to evaluate is to print its size:

newMap <- foldM (\i (k, v) -> (print $ Map.size i) >> (return Map.insert k v i)) Map.empty source

How do I use seq or $! correctly?

More details about my actual problem that this is based off of:

I should note that in my original program, the Map is contained in a TVar and elements are inserted on IO events. So to me it doesn't make sense why if I am modifying the TVar haskell isn't forcing evaluation of the Map. My program is a multithreaded socket server with one address being the "reader" that performs a lookup. The other socket address just pushes bytestrings onto the map. I can connect 2 sockets to the inserting and 1 socket to the reader. if I insert a bunch of times, the map won't be evaluated until my reader socket looksup the value. I would like the Map to be evaluated when I am modifying the TVar. The only way that I found to do this is to print the size of the map before I modify my TVar on an insert.

EDIT:

So I tried a bang pattern, which works on the example above, but doesn't work on my server:

buildIndex :: Int -> ByteString -> M.Map Int ByteString -> M.Map Int ByteString

liftIO $ do indexed <- readTVarIO tvi
            let !newIndex = buildIndex nextKey serialized indexed
            atomically $ writeTVar tvi $! newIndex

Upvotes: 4

Views: 759

Answers (2)

cheecheeo
cheecheeo

Reputation: 692

How about:

liftIO $ atomically $ modifyTVar' (buildIndex nextKey serialized) tvi

instead of

liftIO $ do indexed <- readTVarIO tvi
         let !newIndex = buildIndex nextKey serialized indexed
         atomically $ writeTVar tvi $! newIndex

See the docs for modifyTVar' and what is weak head normal form?

Upvotes: 0

bheklilr
bheklilr

Reputation: 54078

I would recommend using bang patterns, since it's usually the easiest way to force something to evaluate. The problem is that while you're using strict versions of Map and ByteString, not every operation you perform is strict, so source is not getting computed immediately. Instead of trying to track down each statement that's lazily evaluated, just add {-# LANGUAGE BangPatterns #-} to the top of your file and change let newMap = ... to let !newMap = .... That's it.

There are techniques for manually tracking down and eliminating laziness, but bang patterns are by far the easiest and fastest technique that I've seen.

Upvotes: 3

Related Questions