Недавно я познакомился с функциональными зависимостями и семействами типов. Для проекта класса я написал (выполнил) интерпретатор для подмножества C в Java и Haskell. Реализация на Haskell функции оценки для терминов требовала построения «таблиц функций» с явным сопоставлением с образцом и развертыванием конструкторов значений, представляющих литералы. Несчастная ситуация (но гораздо красивее, чем у Java).
Пройдя немного поиска, я наткнулся на пример "коллекций", задаваясь вопросом, могу ли я применить это к моему абстрактному синтаксису для создания общих функций "inject to" и "project from" для литералов. Я придумал две первые попытки теста:
(Использование функциональных зависимостей: функции впрыска и проекции работают без явных аннотаций типов, как и впрыск в Lit при lit . Однако функция проецирования из Lit не будет печатать, выдавая ошибку «не удалось сопоставить ожидаемый тип l
с логическим типом l'
».)
class Prim l a | l -> a, a -> l where
inj :: a -> l
proj :: l -> a
instance Prim LB Bool where inj = LB; proj = lb
instance Prim LI Int where inj = LI; proj = li
data LB = LB {lb :: Bool}
data LI = LI {li :: Int}
data E where Lit :: Prim l a => l -> E
lit :: Prim l a => l -> E
lit = Lit
unlit :: Prim l a => E -> l
unlit (Lit a) = a
(Использование семейств типов. Проблема в том, что я не могу заставить Haskell выводить из аргумента правильный тип возвращаемого значения без явной аннотации, и я не могу написать универсальные функции lit :: Val l -> E
и unlit :: E -> Val l
.)
class Show l => Prim l where
type Val l :: *
inj :: Val l -> l
proj :: l -> Val l
data LB a = LB {lb :: Bool}
data LI a = LI {li :: Int }
instance Prim (LB a) where type Val (LB a) = Bool; inj = LB; proj = lb
instance Prim (LI a) where type Val (LI a) = Int; inj = LI; proj = li;
data E where
Lit :: Prim l => l -> E
Bin :: Op -> E -> E -> E
Я не очень хорошо понимаю семейства типов и плохо разбираюсь в функциональных зависимостях. Но я хотел бы знать две вещи: (а) можно ли набирать и реализовывать нужные мне функции; (б) Если я здесь неправильно понимаю что-то фундаментальное. Это почти работает, но я уже некоторое время борюсь с проверкой типов.
РЕДАКТИРОВАТЬ Это простая модель того, что я хочу, так как было неясно. Класс Bin
реализует функциональность, которую я хочу, в основном. Но я не могу собрать различные «обертываемые и распаковываемые» значения вместе, чтобы сделать из этого AST.
class L t l => Bin t l where
bAp :: (t -> t -> t) -> l -> l -> l
bAp f l r = inj (proj l `f` proj r)
class Show l => L t l | t -> l, l -> t where
inj :: t -> l
proj :: l -> t
typ :: l -> T
instance Bin Int LI
instance Bin Bool LB
instance L Int LI where inj = LI; proj = li; typ = const TI
instance L Bool LB where inj = LB; proj = lb; typ = const TB
data LI = LI {li :: Int} deriving (Eq, Show)
data LB = LB {lb :: Bool} deriving (Eq, Show)
data T = TI | TB | TC | TF | TU deriving (Eq, Show)