Search code examples
haskelldynamictypespolymorphismgeneric-programming

How to specify type of value via 'TypeRep'?


My aim is to write function that takes some polymorphic values and list with typereps representing concrete types. It returns new list with the same values but already casted to concrete types specified via typereps.

Let we have such list of values: ["one", "two"] with -XOverloadedStrings enabled.
Respectively, type of each one is IsString a => a.

List of typereps we could get in such way:

import Data.Typeable (Proxy(..), typeRep)
import Data.Text     (Text)

[typeRep (Proxy :: Proxy String), typeRep (Proxy :: Proxy ByteString)]

Is there any way to get "one" of type String and "two" of type ByteString?

P.S. To prevent error according to list containing values of different types, we may wrap every value in Dynamic., as in the example below(pseudocode):

 {-# LANGUAGE ParallelListComp #-}

 import Data.Dynamic (toDyn)

 [ toDyn (val :: type') | val <- vals | type' <- concreteTypes ]

It could be done using Template Haskell, but it will be too ugly.


Solution

  • I can't really imagine your purpose, but the code will probably look something like this. I'm using the new Type.Reflection interface because I'm more familiar with it than with the classic Data.Typeable, but that should work for this too.

    import Type.Reflection
    
    types :: [SomeTypeRep]
    types = [SomeTypeRep (typeRep @String), SomeTypeRep (typeRep @Text)]
    
    strings :: [String]
    strings = ["one", "two"]
    
    converted :: [Dynamic]
    converted = fromJust $ zipWithM convert types strings
    
    convert :: SomeTypeRep -> String -> Maybe Dynamic
    convert (SomeTypeRep rep) s
      | Just HRefl <- eqTypeRep rep (typeRep @String) = Just $ toDynamic s
      | Just HRefl <- eqTypeRep rep (typeRep @Text) = Just $ toDynamic (fromString s)
      | otherwise = Nothing