Reputation: 7417
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?
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
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
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