Haskell, Lisp и многословие [закрыто]

104

Для тех из вас, кто имел опыт работы как с Haskell, так и с некоторыми разновидностями Lisp, мне любопытно, насколько «приятно» (если использовать ужасный термин) писать код на Haskell и Lisp.

Немного предыстории: сейчас я изучаю Haskell, раньше работал со Scheme и CL (и немного углубился в Clojure). Традиционно меня можно было считать поклонником динамических языков за их краткость и скорость. Я быстро влюбился в макросы Lisp, поскольку они дали мне еще один способ избежать многословия и шаблонности.

Я нахожу Haskell невероятно интересным, поскольку он знакомит меня со способами программирования, о существовании которых я не подозревал. В нем определенно есть некоторые аспекты, которые, как кажется, могут помочь в достижении гибкости, например, простота написания частичных функций. Однако меня немного беспокоит потеря макросов Lisp (я предполагаю, что теряю их; правда, я, возможно, просто еще не узнал о них?) И систему статической типизации.

Не мог бы кто-нибудь, кто сделал приличное количество кода в обоих мирах, прокомментировать, чем отличается опыт, что вы предпочитаете, и если это предпочтение является ситуативным?

Дж. Купер
источник

Ответы:

69

Короткий ответ:

  • почти все, что вы можете сделать с макросами, вы можете сделать с помощью функции высшего порядка (я включаю монады, стрелки и т. д.), но это может потребовать больше размышлений (но только в первый раз, и это весело, и вы будете лучший программист для этого), и
  • статическая система является достаточно общей, чтобы она никогда не мешала вам, и несколько удивительно, что она на самом деле «помогает в достижении гибкости» (как вы сказали), потому что, когда ваша программа компилируется, вы можете быть почти уверены, что она верна, поэтому эта уверенность позволяет вам попробовать из вещей, которые вы иначе могли бы бояться пробовать - в программировании есть «динамическое» ощущение, хотя это не то же самое, что в Lisp.

[Примечание: существует " Template Haskell ", который позволяет писать макросы так же, как в Lisp, но, строго говоря, вам никогда не понадобится .]

ShreevatsaR
источник
15
От Конора МакБрайда, цитируемого Доном Стюартом: «Мне нравится думать о типах как о искажающих нашу гравитацию, так что направление, в котором мы должны двигаться [для написания правильных программ], становится« под гору ». Система типов позволяет удивительно легко писать правильные программы… см. Этот пост и его повторные публикации .
ShreevatsaR
3
Функции высокого порядка не могут заменить макросы, и фактически в CL по какой-то причине есть и то, и другое. Настоящая сила макросов в CL заключается в том, что они позволяют разработчику вводить новые языковые функции, которые помогают лучше выразить решение проблемы, не дожидаясь выхода новой версии языка, как в Haskell или Java. Например, если бы у Haskell была такая возможность, авторам Haskell не нужно было бы писать расширения GHC, если бы они могли быть реализованы самими разработчиками в виде макросов в любое время.
mljrg
@mljrg У вас есть конкретный пример? См. Комментарии к ответу Hibou57 ниже, где предполагаемый пример оказался сомнительным. Мне было бы интересно узнать, что вы имеете в виду (например, код Haskell с макросами и без них).
ShreevatsaR
4
Уберите каррирование из Haskell. Не могли бы вы реализовать это с тем, что осталось бы в Haskell? Другой пример: предположим, что Haskell не поддерживает сопоставление с образцом, могли бы вы добавить его самостоятельно, не требуя от разработчиков GHC поддержки? В CL вы можете использовать макросы для расширения языка по желанию. Я полагаю, что именно поэтому CL язык не изменился со времени своего стандарта еще в 90-х годах, тогда как Haskell, похоже, имеет нескончаемый поток расширений в GHC.
mljrg
64

Прежде всего, не беспокойтесь о потере определенных функций, таких как динамический набор текста. Поскольку вы знакомы с Common Lisp, чрезвычайно хорошо разработанным языком, я предполагаю, что вы знаете, что язык нельзя свести к его набору функций. Все дело в едином целом, не так ли?

В этом отношении Haskell сияет так же ярко, как и Common Lisp. Сочетание его функций дает вам способ программирования, делающий код чрезвычайно коротким и элегантным. Отсутствие макросов несколько смягчается более сложными (но также более сложными для понимания и использования) концепциями, такими как монады и стрелки. Система статических типов увеличивает ваши возможности, а не мешает вам, как в большинстве объектно-ориентированных языков.

С другой стороны, программирование на Haskell намного менее интерактивно, чем на Lisp, и огромное количество отражений, присутствующих в таких языках, как Lisp, просто не соответствует статическому взгляду на мир, который предполагает Haskell. Поэтому доступные вам наборы инструментов для двух языков сильно различаются, но их трудно сравнивать друг с другом.

Я лично предпочитаю Lisp-метод программирования в целом, так как я считаю, что он лучше подходит для моей работы. Однако это не означает, что вы тоже обязаны это делать.

Матиас Бенкард
источник
4
Не могли бы вы подробнее рассказать о «программировании на Haskell гораздо менее интерактивно». Разве GHCi не предоставляет все необходимое?
Йоханнес Герер
3
@JohannesGerer: Я не пробовал, но насколько я прочитал, GHCi не является оболочкой в ​​работающем образе, где вы можете переопределить и расширить произвольные части всей программы во время ее работы. Кроме того, синтаксис Haskell значительно усложняет программное копирование фрагментов программы между repl и редактором.
Svante
13

В Haskell меньше необходимости в метапрограммировании, чем в Common Lisp, потому что многое можно структурировать вокруг монад, а добавленный синтаксис делает встроенные DSL менее древовидными, но всегда есть Template Haskell, как упоминалось ShreevatsaR , и даже Liskell (семантика Haskell + Lisp синтаксис), если вам нравятся круглые скобки.

Herrmann
источник
1
ссылка на Liskell мертва, но сейчас есть Hackett .
Will Ness
10

Что касается макросов, вот страница, на которой рассказывается об этом: Hello Haskell, Goodbye Lisp . Это объясняет точку зрения, согласно которой макросы в Haskell просто не нужны. Для сравнения прилагается небольшой пример.

Пример случая, когда требуется макрос LISP, чтобы избежать оценки обоих аргументов:

(defmacro doif (x y) `(if ,x ,y))

Пример случая, когда Haskell не оценивает систематически оба аргумента без необходимости в каком-либо макроопределении:

doif x y = if x then (Just y) else Nothing

И вуаля

Hibou57
источник
17
Это распространенное заблуждение. Да, в Haskell лень означает, что вам не нужны макросы, когда вы хотите избежать оценки некоторых частей выражения, но это только самое тривиальное подмножество всех применений макросов. Google для "Свинья до Perl" за доклад, демонстрирующий макрос, который нельзя сделать ленивым. Кроме того, если вы хотите, чтобы какой-то бит был строгим, вы не можете сделать это как функцию - отражая тот факт, что Scheme не delayможет быть функцией.
Эли Барзилай,
8
@Eli Barzilay: Я не считаю этот пример очень убедительным. Вот полный и простой перевод слайда 40 с помощью Haskell: pastebin.com/8rFYwTrE
Reid Barton
5
@ Эли Барзилай: Я вообще не понимаю вашего ответа. accept это (E) DSL. acceptФункция является аналогом макроса , изложенным на предыдущих страницах, а также определение vв точности параллельно с определением vв схеме на слайде 40. Функция Haskell и Scheme вычислить то же самое , с той же самой стратегией оценки. В лучшем случае макрос позволяет вам раскрыть оптимизатору большую часть структуры вашей программы. Вряд ли вы можете назвать это примером, когда макросы увеличивают выразительную силу языка способом, который не воспроизводится ленивым вычислением.
Рид Бартон,
5
@Eli Barzilay: В гипотетической ленивой схеме вы могли бы написать это: pastebin.com/TN3F8VVE. Мое общее утверждение состоит в том, что этот макрос мало что дает вам: немного другой синтаксис и более простое время для оптимизатора (но это не имеет значения для «достаточно умный компилятор»). Взамен вы загнали себя в ловушку невыразительного языка; как вы определяете автомат, который соответствует любой букве, не перечисляя их все? Кроме того, я не знаю, что вы имеете в виду, говоря «использовать его во всех подсписках» или «обязательное использование where с его собственной областью действия».
Рид Бартон,
15
Хорошо, я сдаюсь. По-видимому, ваше определение DSL - это «аргументы макроса», и поэтому мой пример с ленивой схемой не является DSL, несмотря на то, что он синтаксически изоморфен оригиналу ( в этой версии automatonстановится letrec, :становится accept, ->становится ничем). Без разницы.
Рид Бартон
9

Я программист на Common Lisp.

Некоторое время назад я попробовал Haskell, и в итоге я решил придерживаться CL.

Причины:

  • динамическая типизация (проверьте динамическую и статическую типизацию - анализ на основе шаблонов от Паскаля Костанцы )
  • необязательные и ключевые аргументы
  • унифицированный синтаксис гомоиконного списка с макросами
  • синтаксис префикса (не нужно помнить правила приоритета)
  • нечистоты и поэтому больше подходят для быстрого прототипирования
  • мощная объектная система с метаобъектным протоколом
  • зрелый стандарт
  • широкий выбор компиляторов

У Haskell, конечно, есть свои достоинства, и некоторые вещи он делает принципиально по-другому, но в долгосрочной перспективе он меня не решает.

Лесли П. Ползер
источник
Привет, у вас случайно есть название той статьи Костанцы, на которую вы ссылались? Похоже, этот файл был перемещен.
michiakig
2
Обратите внимание, что haskell также поддерживает синтаксис префикса, но я бы сказал, что использование monad >> = было бы очень некрасивым. Также я не согласен с тем, что нечистота является благословением: P
альтернатива
2
Мне нравится это примечание: мы еще не собрали эмпирических данных, вызывает ли эта проблема серьезные проблемы в реальных программах.
Джером Баум
22
Ни один из примеров в этой статье (Паскаль Костанца, Динамическая и статическая типизация - анализ на основе шаблонов ) не применим к Haskell. Все они специфичны для Java (или, точнее, для «объектно-ориентированного программирования» ), и я не вижу ни одной из этих проблем, возникающих в Haskell. Точно так же все ваши другие аргументы спорны: можно также сказать, что Haskell «чист и, следовательно, больше подходит для быстрого прототипирования», что синтаксис префикса не является обязательным, что у него нет большого количества компиляторов, которые делают разные вещи и др.
ShreevatsaR
11
Эта статья действительно почти не имеет отношения к Haskell. " dilbert = dogbert.hire(dilbert);" ?? Я сомневаюсь, что многие программисты на Haskell могут даже прочитать это, даже не дергаясь.
leftaround примерно
6

В Haskell вы можете определить функцию if, что невозможно в LISP. Это возможно из-за лени, которая делает программы более модульными. В этой классической статье Джона Хьюза « Почему важны FP » объясняется, как лень улучшает компоновку.

Luntain
источник
5
Схема (один из двух основных диалектов LISP) действительно имеет ленивую оценку, хотя это не по умолчанию, как в Haskell.
Рэнди Воет,
7
(defmacro doif (xy) `(if, x, y))
Джошуа Чик,
4
Макрос не то же самое , как функция - макросы не работают хорошо с функциями высшего порядка , как fold, например , в то время как нестрогие функции делают .
Тихон Джелвис
5

Есть действительно интересные вещи, которых вы можете достичь в Лиспе с помощью громоздких (если возможно) макросов в Haskell. Возьмем, к примеру, макрос «memoize» (см. Главу 9 PAIP Питера Норвига). С его помощью вы можете определить функцию, например foo, а затем просто оценить (memoize 'foo), которая заменяет глобальное определение foo мемоизированной версией. Можете ли вы добиться того же эффекта в Haskell с помощью функций высшего порядка?


источник
3
Не совсем (AFAIK), но вы можете сделать что-то подобное, изменив функцию (при условии, что она рекурсивна), чтобы функция рекурсивно вызывалась
j_random_hacker
6
Вы можете добавить foo в ленивую структуру данных, где значение будет сохранено после вычисления. Фактически это будет то же самое.
Theo Belaire
Все в Haskell запоминается и, вероятно, встраивается, когда это необходимо по умолчанию компилятором Haskell.
aoeu256 01
4

По мере того, как я продолжаю свое путешествие по изучению Haskell, мне кажется, что одна вещь, которая помогает «заменить» макросы, - это способность определять свои собственные инфиксные операторы и настраивать их приоритет и ассоциативность. Довольно сложная, но интересная система!

Дж. Купер
источник