Я немного подозреваю, что это проблема XY, так что вот как бы я решил код, на который вы ссылались. У вас есть следующее:
mapComp :: KVPairs -> IO ()
mapComp kvpairs = do
let init = M.empty
let m = foldr ins init kvpairs where
ins (k, v) t = M.insert k v t
if M.size m /= length kvpairs
then putStrLn $ "FAIL: " ++ show (M.size m) ++ ", " ++ show (length kvpairs)
else pure ()
hashmapComp :: KVPairs -> IO()
hashmapComp kvpairs = do
let init = HML.empty
let m = foldr ins init kvpairs where
ins (k, v) t = HML.insert k v t
if HML.size m /= length kvpairs
then putStrLn $ "Fail: " ++ show (HML.size m) ++ ", " ++ show (length kvpairs)
else pure ()
Здесь много повторений, что обычно нехорошо. Таким образом, мы выделяем биты, которые различаются между двумя функциями, и параметризуем новую функцию этими изменяющимися битами :
-- didn't try to compile this
comp :: mp k v -> (k -> v -> mp k v -> mp k v) -> (mp k v -> Int) -> KVPairs -> IO()
comp h_empty h_insert h_size kvpairs = do
let init = h_empty
let m = foldr ins init kvpairs where
ins (k, v) t = h_insert k v t
if h_size m /= length kvpairs
then putStrLn $ "Fail: " ++ show (h_size m) ++ ", " ++ show (length kvpairs)
else pure ()
Как видите, это действительно механический процесс. Тогда вы звоните, например, comp M.empty M.insert M.size
.
Если вы хотите определить comp
таким образом, чтобы он мог работать с типами карт, о которых вы еще не думали (или которые будут указывать ваши пользователи), то вы должны определить comp
против абстрактного интерфейса. Это делается с помощью классов типов, как в SomeMap
ответе Радроу.
На самом деле вы уже можете выполнить часть этого абстрагирования, заметив, что обе карты, с которыми вы хотите работать, реализуют стандарт Foldable
и Monoid
.
-- didn't try to compile this
comp :: (Foldable (mp k), Monoid (mp k v))=> (k -> v -> mp k v -> mp k v) -> KVPairs -> IO()
comp h_insert kvpairs = do
let init = mempty -- ...also why not just use `mempty` directly below:
let m = foldr ins init kvpairs where
ins (k, v) t = h_insert k v t
if length m /= length kvpairs
then putStrLn $ "Fail: " ++ show (length m) ++ ", " ++ show (length kvpairs)
else pure ()
Как уже упоминалось в комментариях, я думаю, что рюкзак - это (будет?) Способ получить то, что, как вы думаете, вы запрашиваете, то есть параметризованные модули . Я не знаю много об этом, и мне не ясно, какие варианты использования он решает, что вы не захотите использовать более традиционный подход, который я описал выше (возможно, я прочитаю страницу вики).