1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE Rank2Types #-} -- This line isn't required unless you want “import” punning: {-# LANGUAGE NamedFieldPuns #-} -- This can be declared in your API module: import qualified Data.Map as M -- This is used in client code: import Data.Map (Map) -- For convenience. class Default a where def :: a -- To declare the “module”: data DataMap = M -- Choose your module selector… I'll mirror the usual “qualified as M” style for Data.Map. { lookup :: Ord k => k -> Map k a -> Maybe a , insert :: Ord k => k -> a -> Map k a -> Map k a , empty :: forall k a. Map k a } -- This makes using it a lot shorter. Otherwise you can have “dataMap = M { … }”. instance Default DataMap where def = M { lookup = M.lookup , insert = M.insert , empty = M.empty } -- Use the “module”: -- So “let” is OK: foo x y z = let M{..} = def in lookup x $ insert x y z -- I prefer “where”: foo x y z = lookup x $ insert x y z where M{..} = def -- Import punning: foo x y z = lookup x $ insert x y z where M{insert,lookup} = def -- Import renaming: foo x y z = l x $ i x y z where M{insert=i,lookup=l,..} = def -- Exports aren't monomorphic: bar = let M{..} = def in (empty :: Map Int Char,empty :: Map Char Int) -- I think it's quite concise. It doesn't work for types but I think -- types are a rare source of clashes compared to values/functions. -- -- Looking into it further, I see that there doesn't appear to be an -- overhead for this, either: -- -- main = print (lookup 1 (empty :: Map Int Char)) where M{..} = def -- -- In the core -O2 output becomes: -- -- main:main4 :: Int = -- Izh (1::Intzh); -- main:main3 :: (base:DataziMaybe.Maybe -- Char) = -- containerszm0zi3zi0zi0:DataziMap.lookup @ Int -- @ Char base:zdfOrdInt -- main:main4 -- (containerszm0zi3zi0zi0:DataziMap.Tip @ Int -- @ Char); -- -- Or more cleanly, in essence: -- -- main3 = Data.Map.lookup 1 Data.Map.Tip |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | -- Here's an example that deals with ByteString clashes module LS where import Default import qualified Data.ByteString.Lazy as L import GHC.Word data ByteStringLazy = L { pack :: [Word8] -> L.ByteString } instance Default ByteStringLazy where def = L { pack = L.pack} --- module BS where import Default import qualified Data.ByteString as B import GHC.Word data ByteStringStrict = B { pack :: [Word8] -> B.ByteString } instance Default ByteStringStrict where def = B { pack = B.pack} --- module Default where class Default a where def :: a --- {-# LANGUAGE RecordWildCards #-} import BS import Default import LS -- lazy foo = pack [1,2,3] where L{..} = def -- strict bar = pack [1,2,3] where B{..} = def -- mixed zot = (a,b) where a = pack [1,2,3] where L{..} = def b = pack [1,2,3] where B{..} = def |