Я экспериментировал с использованием семейств типов для абстрактного набора инструментов пользовательского интерфейса. Я раскрутился, пытаясь использовать HLists (http://homepages.cwi.nl/~ralf/HList/), чтобы улучшить API.
Первоначально мой API выглядел примерно так:
{-# LANGUAGE TypeFamilies #-}
class UITK tk where
data UI tk :: * -> *
stringEntry :: (UITK tk) => UI tk String
intEntry :: (UITK tk) => UI tk Int
tuple2UI :: (UI tk a,UI tk b) -> (UI tk (a,b))
tuple3UI :: (UI tk a,UI tk b,UI tk c) -> (UI tk (a,b,c))
tuple4UI :: (UI tk a,UI tk b,UI tk c,UI tk d) -> (UI tk (a,b,c,d))
ui :: (UITK tk) => (UI tk (String,Int))
ui = tuple2UI (stringEntry,intEntry)
Это работает, но объединитель UI работает с кортежами, поэтому мне нужна другая функция для каждого размера кортежа. Я думал, что могу использовать что-то вроде HLists, но либо это невозможно, (или, надеюсь), мне просто не хватает нужного типа-фу.
Здесь моя попытка:
{-# LANGUAGE TypeFamilies,FlexibleInstances,MultiParamTypeClasses #-}
-- A heterogeneous list type
data HNil = HNil deriving (Eq,Show,Read)
data HCons e l = HCons e l deriving (Eq,Show,Read)
-- A list of UI fields, of arbitrary type, but constrained on their
-- tk parameter. The StructV associated type captures the return
-- type of the combined UI
class (UITK tk) => FieldList tk l
where type StructV tk l
instance (UITK tk) => FieldList tk HNil
where type StructV tk HNil = HNil
instance (UITK tk, FieldList tk l) => FieldList tk (HCons (UI tk a) l)
where type StructV tk (HCons (UI tk a) l) = (HCons a (StructV tk l))
fcons :: (UITK tk, FieldList tk l) => UI tk a -> l -> HCons (UI tk a) l
fcons = HCons
-- Now the abstract ui toolkit definition
class UITK tk where
data UI tk :: * -> *
stringEntry :: (UITK tk) => UI tk String
intEntry :: (UITK tk) => UI tk Int
structUI :: (FieldList tk l) => l -> (UI tk (StructV tk l))
-- this doesn't work :-(
ui :: (UITK tk) => (UI tk (HCons String (HCons Int HNil)))
ui = structUI (fcons stringEntry
(fcons intEntry
HNil ))
Определение в конце дает мне несколько ошибок, первая из которых:
Z.hs:38:6:
Could not deduce (FieldList
tk (HCons (UI tk0 String) (HCons (UI tk1 Int) HNil)))
arising from a use of `structUI'
from the context (UITK tk)
bound by the type signature for
ui :: UITK tk => UI tk (HCons String (HCons Int HNil))
at Z.hs:(38,1)-(40,21)
Possible fix:
add (FieldList
tk
(HCons
(UI tk0 String) (HCons (UI tk1 Int) HNil))) to the context of
the type signature for
ui :: UITK tk => UI tk (HCons String (HCons Int HNil))
or add an instance declaration for
(FieldList tk (HCons (UI tk0 String) (HCons (UI tk1 Int) HNil)))
In the expression:
structUI (fcons stringEntry (fcons intEntry HNil))
In an equation for `ui':
ui = structUI (fcons stringEntry (fcons intEntry HNil))
Не понимая этого, я думаю, что вижу хотя бы одну из проблем. Я не успеваю сообщить компилятору, что параметры типа 3 tk имеют одинаковый тип (т.е. Он относится к tk, tk0, tk1) выше. Я не понимаю этого: мой конструктор fcons должен поддерживать согласованные параметры UI tk для построенного HList.
Это мой первый опыт работы с типами типов и классами с несколькими параметрами, поэтому, скорее всего, мне не хватает чего-то фундаментального.
Можно ли создать гетерогенные списки с ограниченными элементами? Где я ошибаюсь?