Функциональные зависимости / Тип семейства - AST - PullRequest
2 голосов
/ 10 декабря 2010

Недавно я познакомился с функциональными зависимостями и семействами типов. Для проекта класса я написал (выполнил) интерпретатор для подмножества 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)

Ответы [ 2 ]

4 голосов
/ 10 декабря 2010

То, как вы определили конструктор Lit, предотвратит проецирование содержащегося в нем значения, независимо от того, как вы определяете функцию проекции.

Давайте посмотрим на тип конструктора:

Lit :: Prim l => l -> E

Переменная типа l отображается в параметрах, но не в типе возвращаемого значения.Это означает, что когда вы создаете Lit, вы вводите значение некоторого типа, являющегося членом Prim, а затем навсегда забываете, что его тип был .

Я не уверен, как выхотите исключить сопоставление с образцом и развертывание конструкторов значений.Как правило, у вас есть два варианта выполнения проекций:

  1. Значения проекта во время выполнения, используя сопоставление с образцом или что-то эквивалентное ему.
  2. Спроектируйте значения во время компиляции, доказав с помощью системы типов, что ваш тип данных равен желаемому типу данных.

Есть причины пойти на компиляцию.доказательства времени, но, похоже, у вас нет таких причин.

1 голос
/ 08 февраля 2011

Если вы все еще хотите придерживаться идеи полиморфизма E. Вы можете использовать полиморфные функции:

withUnlit :: E -> (forall l . Prim l => l -> b) -> b
withUnlit (Lit a) f = f a

Но единственное, что вы можете сделать (с чертами, которые вы дали Prim l) это:

showE :: E -> String
showE e = withUnlit e show

А inj и proj. Но у вас нет возможности работать с Val l, кроме как использовать некоторые Data.Dynamic (если я так думаю).

...