Я внедряю небольшой DSL для исследовательского проекта, с которым я связан.Поскольку это пример буквально каждого объяснения GADT, я решил, что сейчас самое время начать их использовать.Очевидные выражения (арифметика и т. Д.) Работали нормально, но мой DSL должен поддерживать пользовательские функции, и именно здесь я столкнулся с проблемой.
Без GADT структура моего типа выражения выглядела нескольковот так (сокращенно до минимального примера):
data Expr = IntConst Int
| BoolConst Bool
| Plus Expr Expr
| Times Expr Expr -- etc
| AndAlso Expr Expr -- etc
| Call String [Expr] -- function call!
Если преобразовать в GADT, как я могу выразить правило Call
?
data Expr a where
IntConst :: Int -> Expr Int
BoolConst :: Bool -> Expr Bool
Plus :: Expr Int -> Expr Int -> Expr Int
Times :: Expr Int -> Expr Int -> Expr Int
AndAlso :: Expr Bool -> Expr Bool -> Expr Bool
Call :: ???
Сначала я подумал, что это невозможнобез некоторых супер причудливых зависимых типов, потому что нет никакого способа узнать, какие типы будет принимать данная пользовательская функция (также тип возвращаемого значения, но я могу это исправить, сделав Call
return Expr a
и специализируясь на строительной площадке).Я могу принудить его к проверке типов, «стерев» тип, добавив правила
EraseInt :: Expr Int -> Expr a
EraseBool :: Expr Bool -> Expr a
, но тогда мне кажется, что я теряю преимущество, связанное с GADT.Я также думал, что может быть какой-то другой rankN
полиморфизм, который я мог бы использовать в Call
(что-то экзистенциальное типа?), Но, похоже, ничего из того, что я придумал, не сработало.
Справка?