订正SlopeOne算法的 Haskell实现

Weighted Slope One in Haskell: collaborative filtering in 29 lines of code by Bryan O'Sullivan 曾经写过一个 SlopeOne 算法的Python版本,后又将其移植到 Haskell版本,但是他的blog上的代码好像有点问题,无法编译通过,特将debug后的版本放出:

module SlopeTwo (Rating, SlopeOne, empty, predict, update) where
import Data.List (foldl')
import qualified Data.Map as M

newtype SlopeOne a = SlopeOne (M.Map a (M.Map a (Int, Double))) 
    deriving (Eq, Show)
type Rating a = M.Map a Double

empty = SlopeOne M.empty
addT (a,b) (c,d) = let (l,r) = (a+c, b+d) in l `seq` r `seq` (l, r)

update :: Ord a => SlopeOne a -> [Rating a] -> SlopeOne a
update (SlopeOne s) = SlopeOne . M.map (M.map norm) . foldl' update' s
  where norm (a,b) = (a, b / fromIntegral a)
        update' s rm = foldl' (\m (k, v) -> insert m k v) s prod
          where prod = [((a, b), (1, m - n)) | (a, m) <- rs, (b, n) <- rs]
                rs = M.toList rm
        insert m (a, b) v = M.alter foo a m
          where foo = Just . maybe (M.singleton b v) (M.insertWith' addT b v)

predict :: Ord a => SlopeOne a -> Rating a -> Rating a
predict (SlopeOne s) rm =
    let 
        freqm = foldl' insert M.empty
                [(a,b,r) | a <- M.keys s, (b, r) <- M.toList rm]
        insert n (a,b,r) = let (f, d) = find s (a, b) in
                           M.insertWith' addT a (f, fromIntegral f * (r + d)) n
        norm = M.map (\(a, b) -> b / fromIntegral a) freqm
        find m (a,b) = M.findWithDefault (0,0) b (M.findWithDefault M.empty a m)
    in M.filterWithKey (\k v -> v > 0 && M.notMember k rm) norm

userData :: [Rating String]
userData = map M.fromList [
            [("squid", 1.0), ("cuttlefish", 0.5), ("octopus", 0.2)],
            [("squid", 1.0), ("octopus", 0.5), ("nautilus", 0.2)],
            [("squid", 0.2), ("octopus", 1.0), ("cuttlefish", 0.4), ("nautilus", 0.4)],
            [("cuttlefish", 0.9), ("octopus", 0.4), ("nautilus", 0.5)]
 ]
prediction = predict (update empty userData) (M.fromList [("squid", 0.4)])