Search code examples
haskellintrospection

Haskell get types of Data Constructor


I was wondering if given a constructor, such as:

data UserType = User
  { username :: String
  , password :: String
  } -- deriving whatever necessary

What the easiest way is for me to get something on the lines of [("username", String), ("password", String)], short of just manually writing it. Now for this specific example it is fine to just write it but for a complex database model with lots of different fields it would be pretty annoying.

So far I have looked through Typeable and Data but so far the closest thing I have found is:

user = User "admin" "pass"
constrFields (toConstr user)

But that doesn't tell me the types, it just returns ["username", "password"] and it also requires that I create an instance of User.


Solution

  • I just knocked out a function using Data.Typeable that lets you turn a constructor into a list of the TypeReps of its arguments. In conjunction with the constrFields you found you can zip them together to get your desired result:

    {-# LANGUAGE DeriveDataTypeable #-}
    
    module Foo where
    import Data.Typeable
    import Data.Typeable.Internal(funTc)
    
    getConsArguments c = go (typeOf c)
       where go x = let (con, rest) = splitTyConApp x    
                    in if con == funTc         
                       then case rest of (c:cs:[]) -> c : go cs
                                         _ -> error "arrows always take two arguments"
                       else []                      
    

    given data Foo = Foo {a :: String, b :: Int} deriving Typeable, we get

    *> getConsArguments Foo
    [[Char],Int]
    

    As one would hope.


    On how to get the field names without using a populated data type value itself, here is a solution:

    constrFields . head . dataTypeConstrs $ dataTypeOf (undefined :: Foo)