Haskell: ссылка на тип значения во время компиляции
Мне интересно, есть ли хороший способ ссылаться на типы значений без явного наложения их с помощью type
в коде (не во время выполнения - здесь не происходит reification).
Возьмите следующий код (используя Data.Vinyl):
{-# LANGUAGE DataKinds, TypeOperators #-}
import Data.Vinyl
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
type Person = ["name" ::: String, "age" ::: Int]
Здесь у нас есть типы "name" ::: String
а также "age" ::: Int
повторяется в двух местах. Если мы повторно используем поля в нескольких записях, это может стать несколькими местами. Несмотря на то, что Person
Тип действительно ссылается на составляющие поля, объявления типов являются независимыми. Так меняется age
быть представленным Float
Скажем, требует перемен в разных местах.
Очевидно, что нет необходимости явно набирать вещи, так как они будут выведены. Однако в моем случае типы записей возвращаются из анализатора параметров и, следовательно, экспортируются. Точно так же можно написать следующее:
type Name = "name" ::: String
name = Field :: Name
type Age = "age" ::: Int
age = Field :: Age
type Person = [Name, Age]
Однако это тогда включает другую загрузку псевдонимов типов и удваивает число строк. Я хотел бы написать следующее:
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
type Person = [typeof name, typeof age]
Это явно связывает тип Person
к типам его полей.
Есть ли способ (желательно sans-TH, но мне было бы интересно даже задействовать TH) сделать это?
1 ответ
Это должно быть достаточно легко сделать String -> [Name] -> DecsQ
функционировать из следующего. Жаль, что с ghc7.6 (по крайней мере) проверка циклов в синонимах типов, кажется, останавливает красивее type Person = $(listOfT ['name, 'age])
от разработки.
{-# 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])