Явний, статичний тип відливання (примус) у Хаскеллі


9

Проблема

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

Мета:

  1. Мають окремий тип змінних та загальних виразів - певні функції можуть застосовуватися до змінних, а не до складних виразів. Наприклад, оператор визначення:= може бути типу, (:=) :: Variable -> Expression -> Definitionі він не повинен бути можливим передавати складний вираз як його лівий параметр (хоча він повинен мати можливість передавати змінну як її правий бічний параметр, без явного введення ) .
  2. Майте вирази примірник Num, щоб можна було просувати цілі літерали до виразів і використовувати зручне позначення для загальних алгебраїчних операцій, таких як додавання чи множення, без введення деяких допоміжних операторів обгортки.

Іншими словами, я хотів би мати неявний і статичний тип передачі (примусу) змінних до виразів. Тепер я знаю, що в такому випадку в Haskell немає чітких типів неявного типу. Тим не менш, певні об'єктно-орієнтовані концепції програмування (просте успадкування в даному випадку) є виразними в системі типів 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.