интерактивно?
Пейтон-Джонс: Ну, сам я сейчас в основном редактирую и компилирую. Но другие просто живут в GHCI.
Сейбел: Когда дело доходит до тестирования, у функциональных языков есть один плюс: если требуется протестировать небольшую функцию, сидящую в глубине программы, надо просто выяснить, что она принимает на входе.
Пейтон-Джонс: Думаю, если входные данные достаточно просты, проблем с моей программой быть не должно. Проблемы возникают, когда GHC пытается скомпилировать какую-нибудь непомерную входную программу и получает неверный ответ.
Тестирование необычайно важно для создания свойств. Очень полезна QuickCheck - библиотека Haskell, генерирующая случайные тесты для функции в зависимости от ее типа. И я старался понять, почему использую QuickCheck - очень приятный инструмент - меньше, чем мог бы. Видимо, потому, что меня беспокоят ситуации, когда трудно сгенерировать тестовые данные. Так или иначе, куча народу создает программы, от которых GHC просто воротит. Для этого у GHC и есть свой багтрекер.
Я обычно начинаю с чего-нибудь, что работает не так. Возможно, компилятор зависнет в каком-то месте, или откажется выполнять программу, которую должен выполнять, или станет генерировать неоптимальный код. Если он просто генерирует плохой код, я гляжу на него на разных стадиях компиляции и соображаю: “Здесь все в порядке, здесь тоже, а здесь нет - в чем дело?”
Сейбел: Как именно вы это делаете?
Пейтон-Джонс: В GHC есть флажки, которые позволяют выводить что-либо на печать.
Сейбел: Встроенные операторы печати для отладки?
Пейтон-Джонс: Да. Плюс к тому структура такая же, как у большинства компиляторов: на верхнем уровне есть конвейерная структура преобразований. Если что-то не так внутри какого-то из шагов, задача усложняется. Но я предпочитаю несложные методы отладки. Покажите мне программу до и после данного шага. Ага, я вижу, в чем ошибка! Если же не вижу, то могу использовать какой-нибудь из небезопасных операторов printf, чтобы понять, что происходит.
Есть разные отладчики для Haskell. Один из них, и просто отличный, написал в этом году студент летней школы, Пепе Иборра: это интерактивный отладчик, который теперь поставляется вместе с GHC. Я, правда, его мало использовал - он появился недавно, и, кроме того, не очень понятно, как пошагово проходить функциональную программу.
Были любопытные исследования насчет отладки функциональных программ. Жаль, что у нас нет простого и очевидного решения, но зато это интересная исследовательская проблема.
Я все это говорю, чтобы показать, что использую крайне примитивные техники отладки, например через небезопасные операторы printf. Тут нечем гордиться. Но долгое время ничего больше не было - по крайней мере, если брать GHC. Я выработал способы, которые делают для меня этот путь самым коротким.
Сейбел: Это как всегда. Зачем создавать новые отладчики, если люди довольствуются операторами печати?
Пейтон-Джонс: Это скорее культурное явление. Если перейти на отладчики платформы .NET, на которые потрачены десятки и сотни тысяч человекочасов, думаю, результат будет качественно иным. Вероятно, для хорошей работы отладчики требуют еще больше циклов разработки. Но зато в итоге получается образцово полезная вещь.
Возможно, вы разговаривали в основном с людьми академического склада и с теми, кто в силу возраста не привык к сложным отладчикам. Я бы не стал делать никаких общих выводов. И, конечно, не хочу принизить значение качественных отладчиков — особенно для сложных систем с множеством программных слоев. GHC очень прост сравнительно со средой .NET, где есть слои DOM и UML, и не знаю, что еще. Теперь вокруг столько примочек, что программная поддержка становится действительно важной.
Сейбел: Еще один способ создавать правильные программы - формальные доказательства. Что вы думаете об их полезности?
Пейтон-Джонс: Представьте, что ваша цель - иметь для всего автоматическую проверку правильности. Что это будет означать? Проверка по сравнению с чем? По сравнению с некоей спецификацией. Что за спецификация? Она должна описывать
Я тут немного хитрю, ведь в спецификации вы можете сказать то, чего не скажете в программе. Например, “результатом функции является такое
Более продуктивным для практических целей будет описание некоторых
Как их написать? Функциональные языки неплохо приспособлены для этого. Именно это и происходит, если писать спецификацию в Quick-Check - свойства получаются функциями языка Haskell. Допустим, мы хотим проверить, что функция reverse является своей противоположностью, тогда мы напишем check reverse с типом список из А -> булевское значение. Итак, checkreverse от xs будет: reverse от reverse xs равно xs. Это функция, которая всегда оказывается верной. Функция-свойство. Но она написана на том же самом языке, и это здорово.
Теперь можно думать о статических проверках этого. Это может быть трудно, а может и легко. Но все равно, если свойство записано по всем правилам, это большое облегчение. Можно проверить его путем генерирования тестовых данных - именно это делает QuickCheck.
По-моему, писать частичные спецификации гораздо плодотворнее, чем одну спецификацию на
Сейбел: Вы определяете много свойств для всех важных, по-вашему, вещей. Потом по возможности проводится статическая либо динамическая проверка. Ведь мы не сможем устроить статическую проверку для них всех?
Пейтон-Джонс: Правильно. Но в функциональном мире у вас больше шансов. Однако мы все равно слишком долго раскачиваемся, чтобы продемонстрировать это. Так или иначе, первое - это записать свойства.
Мне кажется очень важным уйти от этого глобального подхода, “все или ничего”, в сторону очень полезного статического и динамического тестирования частичных спецификаций. Это повысит вашу уверенность в правильности программы, а на большее рассчитывать нельзя. Даже так называемые полные спецификации не учитывают вещи вроде того, что программа должна работать за 0,1 с или занимать не более 10 Кбайт памяти. Часто в них ничего не говорится о ресурсах, о времени. Даже если программа формально отвечает спецификации, из-за этих мелочей она может работать не так, как нужно. Думаю, мы обманываем себя, когда говорим, что проверили программу и все в порядке. Лучше честно сказать, что мы повышаем свою уверенность в ней. Тут все может начинаться скромно - вы повышаете уверенность на 75%, прикладывая всего 5% от общего количества усилий. Это хороший показатель.
Сейбел: Поговорим о параллелизме. Гай Стил поручил мне задать вам вопрос о транзакционной памяти - спасет она мир или все же нет?
Пейтон-Джонс: Нет, конечно. Сама по себе - нет. Параллелизм - многоголовый