Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use IntMap with different key types?

Tags:

types

haskell

I have a record in a document that use two IntMaps:

data Doc = Doc { kernels :: IntMap Kernel, nodes :: IntMap Node }

But I found that the keys from both IntMaps have different meaning and I fail to separate in two differents types and don't get errors when mix kernel types and node types. I want to have functions that check the the keys from kernel map and node maps and don't allow mix-up. E.g:

someFunction :: Doc -> KernelKey -> NodeKey -> a
someFunction doc k1 k2 = .....

Instead of current:

someFunction :: Doc -> Int -> Int -> a
someFunction doc k1 k2 = .... -- warning check twice k1 and k2

Is it posible? Or I shall change from IntMap to Map.

Thanks

like image 661
Zhen Avatar asked Aug 01 '11 17:08

Zhen


2 Answers

You can use newtype to make wrappers around Int to distinguish their meaning.

newtype KernelKey = KernelKey Int
newtype NodeKey = NodeKey Int

someFunction :: Doc -> KernelKey -> NodeKey -> a
someFunction doc (KernelKey k1) (NodeKey k2) = ...

That way, you can still use IntMap internally, but expose a more type-safe interface, especially if you also control how the KernelKey and NodeKey values get created, i.e. you don't export their constructors so users only get them as return values from your other functions.

Note that newtype wrappers disappear at run time, so this extra wrapping and unwrapping does not affect performance in any way.

like image 100
hammar Avatar answered Nov 12 '22 10:11

hammar


You could create a unified key type and wrap the IntMap API for your Doc type. It might look something like this.

data DocValue = DocKernel Kernel
              | DocNode Node
data DocKey = KernelKey Int
            | NodeKey Int

docLookup :: DocKey -> Doc -> Maybe DocValue

The nice thing about that solution is that you only need one copy of each of the map API functions that you need. Here's a different solution which is closer to the code you have.

newtype NodeKey = NodeKey Int
newtype KernelKey = KernelKey Int
lookupDocNode :: NodeKey -> Doc -> Maybe Node
lookupDocKernel :: KernelKey -> Doc -> Maybe Kernel

You could also do this solution without the newtypes. Both these solutions give you type safety. In the first one you have to specify what you want with types. In the second one you specify it by choosing which function to call.

like image 27
mightybyte Avatar answered Nov 12 '22 10:11

mightybyte