Haskell如何强制评估haskell中的Data.Map?

时间:2013-08-30 00:20:54

标签: haskell

所以我有一张严格的地图

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]

我每次放一个元素时都试图让Map进行评估......

 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

但是,在我进入“交互模式”并提交查询之前,地图不会评估。

我能评估它的唯一方法是打印它的大小:

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

如何正确使用seq$!

关于我的实际问题的更多细节,这是基于:

我应该注意,在我的原始程序中,Map包含在TVar中,并且元素被插入到IO事件中。所以对我而言,如果我正在修改TVar haskell并不强制评估Map,那就没有意义。我的程序是一个多线程套接字服务器,其中一个地址是执行查找的“读取器”。另一个套接字地址只是将字节串推送到地图上。我可以将2个插座连接到插头,将1个插座连接到阅读器。如果我插入很多次,在我的阅读器套接字查找值之前,不会评估地图。我希望在修改TVar时评估Map。我发现这样做的唯一方法是在我在插页上修改我的TVar之前打印地图的大小。

编辑:

所以我尝试了一个爆炸模式,它适用于上面的例子,但在我的服务器上不起作用:

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

2 个答案:

答案 0 :(得分:3)

我建议使用爆炸模式,因为它通常是迫使某些东西进行评估的最简单方法。问题是,当您使用严格版本的MapByteString时,并非您执行的每个操作都是严格的,因此源不会立即计算。不要试图追踪懒惰评估的每个语句,只需将{-# LANGUAGE BangPatterns #-}添加到文件顶部,然后将let newMap = ...更改为let !newMap = ...。就是这样。

有手动追踪和消除懒惰的技术,但是爆炸模式是迄今为止我见过的最简单,最快速的技术。

答案 1 :(得分:0)

怎么样:

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

而不是

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

请参阅modifyTVar'what is weak head normal form?

的文档