aga*_*gam 10 haskell haskell-backpack
这似乎是人为的,但是我似乎找不到以下答案:
说我有以下进口:
import qualified Data.Map as M
import qualified Data.HashMap.Lazy as HML
Run Code Online (Sandbox Code Playgroud)
现在,我有了一些函数(comp),该函数需要一些列表,执行某些操作,创建地图并返回它。
我的问题是我如何有两种调用方式,comp以便正确调用insert并size映射?
作为一名稻草人,我可以编写此函数的两个副本,一个引用M.insert和M.size,而另一个引用HML.insert和HML.size...,但是我如何“将模块作为参数传递”,或者另外指出呢?
谢谢!
编辑:为了使这个不太抽象,这些是的确切定义comp:
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 ()
Run Code Online (Sandbox Code Playgroud)
编辑(2):这竟然是方式更有趣的比我预期的,感谢大家谁回答!
这是使用模块签名和Mixins(又名Backpack)的方法
您将必须定义一个具有以下签名的库(它可以是内部库):
-- file Mappy.hsig
signature Mappy where
class C k
data Map k v
empty :: Map k v
insert :: C k => k -> v -> Map k v -> Map k v
size :: Map k v -> Int
Run Code Online (Sandbox Code Playgroud)
在同一个库中或在另一个库中,编写将签名当作普通模块导入的代码:
module Stuff where
import qualified Mappy as M
type KVPairs k v = [(k,v)]
comp :: M.C k => KVPairs k v -> IO ()
comp 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 ()
Run Code Online (Sandbox Code Playgroud)
在另一个库中(必须是另一个库),编写一个与签名匹配的“实现”模块:
-- file Mappy.hs
{-# language ConstraintKinds #-}
module Mappy (C,insert,empty,size,Map) where
import Data.Map.Lazy
type C = Ord
Run Code Online (Sandbox Code Playgroud)
“签名匹配”仅基于名称和类型执行,实现模块不需要知道签名的存在。
然后,在要使用抽象代码的库或可执行文件中,将具有抽象代码的库和具有实现的库同时拉出:
executable somexe
main-is: Main.hs
build-depends: base ^>=4.11.1.0,
indeflib,
lazyimpl
default-language: Haskell2010
library indeflib
exposed-modules: Stuff
signatures: Mappy
build-depends: base ^>=4.11.1.0
hs-source-dirs: src
default-language: Haskell2010
library lazyimpl
exposed-modules: Mappy
build-depends: base ^>=4.11.1.0,
containers >= 0.5
hs-source-dirs: impl1
default-language: Haskell2010
Run Code Online (Sandbox Code Playgroud)
有时签名的名称和实现模块的名称不匹配,在这种情况下,必须使用Cabal文件的mixins部分。
编辑。创建HashMap实现被证明有些棘手,因为insert需要两个约束(Eq和Hashable)而不是一个。我不得不诉诸“类同义词”的把戏。这是代码:
{-# language ConstraintKinds, FlexibleInstances, UndecidableInstances #-}
module Mappy (C,insert,HM.empty,HM.size,Map) where
import Data.Hashable
import qualified Data.HashMap.Strict as HM
type C = EqHash
class (Eq q, Hashable q) => EqHash q -- class synonym trick
instance (Eq q, Hashable q) => EqHash q
insert :: EqHash k => k -> v -> Map k v -> Map k v
insert = HM.insert
type Map = HM.HashMap
Run Code Online (Sandbox Code Playgroud)
最简单的方法是根据您实际需要的操作而不是模块进行参数化。所以:
mapComp ::
m ->
(K -> V -> m -> m) ->
(m -> Int) ->
KVPairs -> IO ()
mapComp empty insert size kvpairs = do
let m = foldr ins empty kvpairs where
ins (k, v) t = insert k v t
if size m /= length kvpairs
then putStrLn $ "FAIL: " ++ show (size m) ++ ", " ++ show (length kvpairs)
else pure ()
Run Code Online (Sandbox Code Playgroud)
然后可以将其称为,例如mapComp M.empty M.insert M.size或mapComp HM.empty HM.insert HM.size。作为一个小的附带好处,即使调用者喜欢的数据结构没有通过编写小型适配器并将其传递给其提供的名称和类型正确的模块,也可以使用此功能。
如果愿意,可以将它们合并为一条记录,以简化传递过程:
data MapOps m = MapOps
{ empty :: m
, insert :: K -> V -> m -> m
, size :: m -> Int
}
mops = MapOps M.empty M.insert M.size
hmops = MapOps HM.empty HM.insert HM.size
mapComp :: MapOps m -> KVPairs -> IO ()
mapComp ops kvpairs = do
let m = foldr ins (empty ops) kvpairs where
ins (k, v) t = insert ops k v t
if size ops m /= length kvpairs
then putStrLn "Yikes!"
else pure ()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
217 次 |
| 最近记录: |