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.
I just knocked out a function using Data.Typeable
that lets you turn a constructor into a list of the TypeRep
s 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)