I'm wondering if there's a nice way to refer to types of values without explicitly aliasing them using type
in code (not at runtime - there is no reification going on here).
Take the following code (using Data.Vinyl):
{-# LANGUAGE DataKinds, TypeOperators #-}
import Data.Vinyl
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
type Person = ["name" ::: String, "age" ::: Int]
Here we have the types "name" ::: String
and "age" ::: Int
repeated in two places. If we reuse fields in multiple records, this can become multiple places. Despite the fact that the Person
type is really referring to the constituent fields, the type declarations are independent. So changing age
to be represented by Float
, say, requires changes in various places.
Obviously it's not necessary to explicitly type things, since they will be inferred. However, in my case the record types are being returned from an options parser, and thus exported. Likewise, one could write the following:
type Name = "name" ::: String
name = Field :: Name
type Age = "age" ::: Int
age = Field :: Age
type Person = [Name, Age]
However, this then involves another load of type aliases and double the number of lines. What I would like to be able to write is the following:
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
type Person = [typeof name, typeof age]
This explicitly links the type of a Person
to the types of its fields.
Is there a way (preferably sans-TH, but I'd be interested even involving TH) to do this?
It should be easy enough to make a String -> [Name] -> DecsQ
function out of
the following. Too bad with ghc7.6 (at least), the check for cycles in type
synonyms seems to stop the prettier type Person = $(listOfT ['name, 'age])
from
working out.
{-# LANGUAGE DataKinds, TemplateHaskell, TypeOperators #-}
import Language.Haskell.TH
import Control.Applicative
import Data.Vinyl
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
let listOfT (n:ns) = do
VarI _ ty _ _ <- reify n
(appT promotedConsT) (return ty) `appT` listOfT ns
listOfT [] = promotedNilT
in return <$> tySynD (mkName "Person") [] (listOfT ['name, 'age])