проблема
Рассмотрим следующую проблему дизайна в Haskell. У меня есть простой, символический EDSL, в котором я хочу выразить переменные и общие выражения (многомерные полиномы), такие как x^2 * y + 2*z + 1
. Кроме того, я хочу выразить некоторые символические уравнения над выражениями, скажем x^2 + 1 = 1
, а также определениями , например x := 2*y - 2
.
Цель состоит в том, чтобы:
- Имейте отдельный тип для переменных и общих выражений - некоторые функции могут применяться к переменным, а не к сложным выражениям. Например, оператор определения
:=
может иметь тип,(:=) :: Variable -> Expression -> Definition
и не должно быть возможности передавать сложное выражение в качестве параметра левой части (хотя должна быть возможность передавать переменную в качестве параметра правой части без явного приведения ). , - Имейте выражения в качестве экземпляра
Num
, чтобы можно было добавлять целочисленные литералы в выражения и использовать удобные обозначения для общих алгебраических операций, таких как сложение или умножение, без введения некоторых вспомогательных операторов-оболочек.
Другими словами, я хотел бы иметь неявное и статическое приведение типов (приведение) переменных к выражениям. Теперь я знаю, что в Хаскеле нет неявных приведений типов. Тем не менее, некоторые объектно-ориентированные концепции программирования (простое наследование, в данном случае) являются выразимы в системе типов в Haskell, с или без расширений языка. Как я могу удовлетворить оба вышеупомянутых пункта, сохраняя легкий синтаксис? Это вообще возможно?
обсуждение
Понятно, что главной проблемой здесь является Num
ограничение типа, например
(+) :: Num a => a -> a -> a
В принципе, можно написать один (обобщенный) алгебраический тип данных для переменных и выражений. Тогда можно было бы написать :=
таким образом, что левостороннее выражение распознается, и принимается только конструктор переменной, в противном случае возникает ошибка времени выполнения. Это, однако, не чистое, статичное (то есть время компиляции) решение ...
пример
В идеале я хотел бы получить легкий синтаксис, такой как
computation = do
x <- variable
t <- variable
t |:=| x^2 - 1
solve (t |==| 0)
В частности, я хочу запретить нотацию, например, t + 1 |:=| x^2 - 1
так как она
:=
должна давать определение переменной, а не целое выражение в левой части.
FromVar
класс типов будет полезен. Я хочу избежать явных приведений при сохранении Expr
экземпляра Num
. Я отредактировал вопрос, добавив пример нотации, которую хотел бы получить.
class FromVar e
метод сfromVar :: Variable -> e
и предоставить экземпляры дляExpression
иVariable
, затем ваши переменные имеют полиморфные типыx :: FromVar e => e
и т. д. Я не проверял, насколько хорошо это работает, так как я сейчас на моем телефоне.