Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I parameterize a function by module in Haskell?

This might seem artificial, but I can't seem to find an obvious answer to the following:

Say I have the following imports:

import qualified Data.Map as M
import qualified Data.HashMap.Lazy as HML

Now I have some function (comp) that takes some list, does something, creates a map, returns it.

My question is how do I have two ways of calling comp so that its calls (say) to insert and size map correctly?

As a strawman, I could write two copies of this function, one referencing M.insert and M.size, while the other references HML.insert and HML.size ... but how do I "pass the module as a parameter", or indicate this otherwise?

Thanks!

Edit: to make this less abstract these are the exact definitions of 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 ()

Edit (2): this turned out to be way more interesting than I anticipated, thanks to everyone who responded!

like image 989
agam Avatar asked Mar 07 '19 22:03

agam


People also ask

What does ++ do Haskell?

The ++ operator is the list concatenation operator which takes two lists as operands and "combines" them into a single list.

What is module Main where in Haskell?

A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages.

What does in do in Haskell?

in goes along with let to name one or more local expressions in a pure function.

How do I use just in Haskell?

If you want to indicate that there's no value, return Nothing . If you want to return a value bar , you should instead return Just bar . So basically, if you can't have Nothing , you don't need Just . If you can have Nothing , you do need Just .


1 Answers

The simplest is to parameterize by the operations you actually need, rather than the module. So:

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 ()

You can then call it as, e.g. mapComp M.empty M.insert M.size or mapComp HM.empty HM.insert HM.size. As a small side benefit, callers may use this function even if the data structure they prefer doesn't offer a module with exactly the right names and types by writing small adapters and passing them in.

If you like, you can combine these into a single record to ease passing them around:

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 ()
like image 169
Daniel Wagner Avatar answered Oct 23 '22 05:10

Daniel Wagner