52700.fb2
ValTrue
-> True
ValFalse
-> False
If p t e
-> if eval p then eval t else eval e
Val n
-> n
Add a b
-> eval a + eval b
Mul a b
-> eval a * eval b
Если eval получит логическое значение, то будет возвращено значение типа Bool, а на значение типа Exp
Int будет возвращено целое число. Давайте убедимся в этом:
Расширения | 255
*Prelude> :l Exp
[1 of 1] Compiling Exp
( Exp. hs, interpreted )
Ok, modules loaded: Exp.
*Exp> let notE x = If x ValFalse ValTrue
*Exp> let squareE x = Mul x x
*Exp>
*Exp> eval $ squareE $ If (notE ValTrue) (Val 1) (Val 2)
4
*Exp> eval $ notE ValTrue
False
*Exp> eval $ notE $ Add (Val 1) (Val 2)
< interactive>:1:14:
Couldn’t match expected type ‘Bool’ against inferred type ‘Int’
Expected type: Exp Bool
Actual type: Exp Int
In the return type of a call of ‘Add’
In the second argument of ‘($)’, namely ‘Add (Val 1) (Val 2)’
Сначала мы определили две вспомогательные функции. Затем вычислили несколько значений. Haskell
очень часто применяется для построения компиляторов. Мы рассмотрели очень простой язык, но в более
сложном случае суть останется прежней. Дополнительный параметр позволяет нам закодировать в парамет-
ре тип функций нашего языка. Спрашивается: зачем нам дублировать вычисления в функции eval? Зачем нам
сначала кодировать выражение конструкторами, чтобы только потом получить то, что мы могли вычислить
и напрямую.
При таком подходе у нас есть полный контроль за деревом выражения, мы можем проводить дополни-
тельную оптимизацию выражений, если нам известны некоторые закономерности. Ещё функция eval может
вычислять совсем другие значения. Например она может по виду выражения составлять код на другом языке.
Возможно этот язык гораздо мощнее Haskell по вычислительным способностям, но беднее в плане вырази-
тельности, гибкости синтаксиса. Тогда мы будем в функции eval проецировать разные конструкции Haskell
в конструкции другого языка. Такие программы называются предметно-ориентированными языками програм-
мирования (domain specific languages). Мы кодируем в типе Exp некоторую область и затем надстраиваем
над типом Exp разные полезные функции. На самом последнем этапе функция eval переводит всё дерево
выражения в значение или код другого языка.
Отметим, что не так давно было предложено другое решение этой задачи. Мы можем закодировать типы