Изучение внутреннего устройства функций в Haskell
-
23-08-2019 - |
Вопрос
Я новичок в Haskell, хотя ранее имел опыт работы с Lisp/Scheme.Прямо сейчас я просматриваю примеры из SICP и пытаюсь реализовать их на Haskell, чтобы получить больше практического опыта.В лекции 3б авторы представляют функцию для символьного вычисления производных.Он содержит, среди прочего, следующие строки:
(define (deriv exp var)
(cond ((constant? exp var) 0)
((same-var? exp var) 1)
; ...
Далее в лекции определены еще некоторые функции:
(define (constant? exp var)
(and (atom? exp)
(not (eq? exp var))))
Есть ли способ сделать то же самое в Haskell, т.е.проверить атомарность и символическую эквивалентность какой-либо другой функции?Или, в более общем плане, каковы средства «дизассемблирования» функций в Haskell?
Решение
Ваши примеры Scheme на самом деле не исследуют функции Scheme.Недавно я провел в Haskell символическую дифференциацию значений следующего типа:
data Exp a = Lit a
| Exp a :*: Exp a
| Exp a :+: Exp a
| Var String
deriving Eq
Вместо того, чтобы различать использование atom?
или eq?
ты используешь case
(или другое сопоставление с образцом) и ==
.
Другие советы
Во-первых, хотя SICP великолепен, я бы не рекомендовал его для изучения Haskell. (#) Некоторые трудности в этом вопросе проистекают из этого.
В Lisp/Scheme 'функция' рассматривается как фрагмент кода, а изучение функции означает просто изучение ее кода.В Хаскеле 'функция' означает нечто более близкое к своему математическому определению, как отображение множества А в множество Б.Так, например, в контексте Лиспа имеет смысл сравнить две функции:просто сравните их код.(Но (x+y)^2
и x^2+2*x*y+y^2
разные функции?) В Haskell это зависит от того, существует ли конструктивная процедура определения равенства для рассматриваемого вами класса функций.
Точно так же, как и в вашем вопросе, в Lisp/Scheme вы должны написать функцию «выведения», которая правильно различает заданные выражения и просто выдает ошибку или возвращает мусор на произвольных входных данных.В системе типов Haskell это (AFAIK) невозможно сделать, потому что — если вдуматься — не существует такой вещи, как дифференцирование произвольного ввода:вы можете дифференцировать только выражение (или, возможно, более общий класс, но еще не все).Итак, как и в ответе Нормана Рэмси, вы сначала определяете тип «Выражение» (или класс типа), что очень просто сделать, а затем пишете функцию
derive :: Expression -> Expression
который разбирает Expression
используя конструкции сопоставления с образцом (или что-то еще, в зависимости от того, как Expression
были застроены).
(#):Причина в том, что у SICP совершенно другая философия, которая предполагает использование нетипизированного языка программирования и поощрение отсутствия различия между кодом и данными.Хотя аргумент «код=данные» имеет некоторые преимущества (например,тот факт, что в архитектуре фон Неймана, которую мы используем, «все равно все 0 и 1»), это не обязательно хороший способ рассуждать или моделировать проблемы.(См. книгу Филипа Уодлера. Почему расчет лучше, чем планирование подробнее об этом.) Если вы хотите прочитать книгу о Haskell с функциональным вкусом вместо Реальный мир один, возможно, Саймона Томпсона Хаскелл:Мастерство функционального программирования или Ричарда Берда Введение в функциональное программирование с использованием Haskell это лучший выбор.
Я не думаю, что ты сможешь это сделать.Лисп - это гомоиконический, Хаскель - нет.
Однако дальнейшее гугление всплыло Лискелл, который является (?) интересным гибридом.