Неявное приведение статического типа (приведение) в Haskell


9

проблема

Рассмотрим следующую проблему дизайна в Haskell. У меня есть простой, символический EDSL, в котором я хочу выразить переменные и общие выражения (многомерные полиномы), такие как x^2 * y + 2*z + 1. Кроме того, я хочу выразить некоторые символические уравнения над выражениями, скажем x^2 + 1 = 1, а также определениями , например x := 2*y - 2.

Цель состоит в том, чтобы:

  1. Имейте отдельный тип для переменных и общих выражений - некоторые функции могут применяться к переменным, а не к сложным выражениям. Например, оператор определения:= может иметь тип, (:=) :: Variable -> Expression -> Definitionи не должно быть возможности передавать сложное выражение в качестве параметра левой части (хотя должна быть возможность передавать переменную в качестве параметра правой части без явного приведения ). ,
  2. Имейте выражения в качестве экземпляра 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так как она :=должна давать определение переменной, а не целое выражение в левой части.


1
может быть, вы могли бы использовать class FromVar eметод с fromVar :: Variable -> eи предоставить экземпляры для Expressionи Variable, затем ваши переменные имеют полиморфные типы x :: FromVar e => eи т. д. Я не проверял, насколько хорошо это работает, так как я сейчас на моем телефоне.
Мор А.

Я не уверен, как FromVarкласс типов будет полезен. Я хочу избежать явных приведений при сохранении Exprэкземпляра Num. Я отредактировал вопрос, добавив пример нотации, которую хотел бы получить.
Мацей Бендковски

Ответы:


8

Чтобы использовать полиморфизм, а не подтипы (потому что это все, что у вас есть в Haskell), не думайте, что «переменная является выражением», но «у переменных и выражений есть некоторые общие операции». Эти операции могут быть помещены в класс типов:

class HasVar e where fromVar :: Variable -> e

instance HasVar Variable where fromVar = id
instance HasVar Expression where ...

Тогда, вместо того, чтобы кастовать вещи, сделайте вещи полиморфными. Если у вас есть v :: forall e. HasVar e => e, его можно использовать как в качестве выражения, так и в качестве переменной.

example :: (forall e. HasVar e => e) -> Definition
example v = (v := v)  -- v can be used as both Variable and Expression

 where

  (:=) :: Variable -> Expression -> Definition

Скелет, чтобы сделать код ниже проверки типа: https://gist.github.com/Lysxia/da30abac357deb7981412f1faf0d2103

computation :: Solver ()
computation = do
  V x <- variable
  V t <- variable
  t |:=| x^2 - 1
  solve (t |==| 0)

Интересно, спасибо! Я решил на короткое время спрятать как переменные, так и выражения за экзистенциальными типами, однако я отверг эту идею, потому что она вводила дополнительные обозначения, смотрите ваши V. Первоначально это не то, что я хотел, но, возможно, я был слишком быстр, чтобы отклонить это ... Возможно, я не могу избавиться от непрозрачного V. На соответствующей заметке, как я могу создать экземпляр V (forall e . HasVar e => e)? В Coq я бы использовал вычисления типов и сопоставление с образцом для индуктивного типа, но неясно, как этого добиться в Haskell.
Мацей Бендковски

1
Вы можете получить w :: Variableкак-нибудь и применить fromVarк нему variable = (\w -> V (fromVar w)) <$> (_TODO_ :: Solver Variable).
Ли-Яо Ся

1
И Vможно было бы избежать с помощью непредсказуемых типов, но это все еще WIP. Или мы можем сделать variableпродолжение с полиморфным аргументом явно, а не косвенно через (>>=).
Ли-Яо Ся
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.