Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

A map of polymorphic values in Haskell

Suppose I’ve got a class, which declares some constructors for values of its member types:

class C t where
  fromInt :: Int -> t
  fromString :: String -> t

Further suppose, I want to create a bunch of values using these polymorphic constructors and put them in a Map from containers package. But importantly, I want the values to remain polymorphic and deferr the decision on their concrete type until they are extracted from the map.

newtype WrapC = WrapC (forall t . C t => t)

newtype MapC = MapC (Map String WrapC)

Using RankNTypes extension I can define such a map and the following definition witnesses the fact that polymorphic values indeed can be extracted from it:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

get :: C t => String -> MapC -> Maybe t
get key (MapC m) = fmap (\(WrapC t) -> t) $ Map.lookup key m

However, when I want to add a value to the map, I’m facing a type error, because Haskell apparently cannot unify some hidden type variables. The definition:

add :: C t => String -> t -> MapC -> MapC
add key val (MapC m) = MapC $ Map.insert key (WrapC val) m

fails to compile with:

• Couldn't match expected type ‘t1’ with actual type ‘t’
  ‘t1’ is a rigid type variable bound by
    a type expected by the context:
      forall t1. C t1 => t1
    at src/PolyMap.hs:21:53-55
  ‘t’ is a rigid type variable bound by
    the type signature for:
      add :: forall t. C t => String -> t -> MapC -> MapC

Intuitively I’m guessing that’s the type variable hidden inside WrapC that cannot be unified. What I don’t understand is why it needs to be. Or how can I make this example work…

>Solution :

You just need to hand your function something actually polymorphic:

add :: String -> (forall t. C t => t) -> MapC -> MapC

But I would propose something even dumber, maybe. Can you get away with this?

type MapC = Map String (Either Int String)

get :: C t => String -> MapC -> Maybe t
get k m = either fromInt fromString <$> Map.lookup k m

No type system extensions needed.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading