На RSDN дали ссылку на интервью Джона Хьюза (вот прямая ссылка на интервью) – одного из разработчиков языка Haskell и автора инструмента QuickCheck.
Дядька явно умный, представитель т.н. академических кругов. Местами здравомыслящий:
Например, если вы посмотрите на Haskell, то есть определенные области программирования, в которых Haskell по настоящему превосходен, и есть другие области, в которых он совершено не подходит. Так, я не думаю, что Haskell полностью заменит C.
С другой стороны, он говорит:
…когда я начинал, мейнстримом были, наверное, Fortran и COBOL, и даже C был довольно новой штукой в то время. Пионеры функционального программирования говорили об увеличении производительности на порядок и я думаю, что функциональное программирование это обеспечило.
Если вы сравните Haskell-программы с C-кодом или даже C++ то часто они на порядок меньше и проще. Тоже самое и для Erlang, что сейчас находит подтверждение в индустрии.
Как-то это не очень согласуется между собой. Разве что во второй цитате он говорит об областях, где у Haskell/Erlang нет конкурентов. Что свойственно ученым: из всех имеющихся фактов ученым свойственно упоминать лишь те, которые доказывают их гипотезу ;) (старая шутка).
Хьюз говорит, что Haskell очень хорошо позволяет создавать DSL-и и это является одной из сильных сторон языка. Как по мне, так DSL-и (особенно internal DSL-и) не есть хорошо. Да, сейчас вокруг них существует много шума. И в каких-то областях DSL-и действительно очень полезны и важны (те же Regexp-ы или IDL-описания). Но это эпизодические случаи, которые имеют очень четкие границы применения. Однако, имхо, мало кто знает, во что превратиться программирование, когда большие программы будут собирать из десятков и даже сотен разношерстных DSL (как сейчас это происходит с библиотеками). Lisp-ы вот так же были хороши в DSL-естроении…
Довольно смешно было читать про то, как перейдя к программированию в среде с побочными эффектами (коей является Erlang), ему пришлось вспомнить, как же тяжело выискивать баги, вызванные побочными эффектами. Мол, столько лет я писал на чисто функциональном Haskell-е, а тут в Erlang-е приходится по нескольку часов заниматься отладкой, чтобы найти багу…
Да, кстати, приходится программировать на Erlang-е Хьюзу потому, что у Erlang-овской версии QuickCheck есть пользователи. Тогда как на Haskell-е в индустрии не больно-то заработаешь ;)
Вот чего я не понял в его рассказе, так это следующего фрагмента:
…если я хочу использовать stateful-библиотеку, то я обычно создаю над ней интерфейс без побочных эффектов, такой, что я могу безопасно использовать его в остальном моем коде.
Не представляю я, как над кодом, задачей которого является производство побочных эффектов, можно построить интерфейс без побочных эффектов? Туп-туп. Буду признателен, если кто-нибудь сможет разъяснить мне это на пальцах или хотя бы выскажет идею на этот счет.
5 комментариев:
> Как по мне, так DSL-и (особенно internal DSL-и) не есть хорошо.
Я тебе по секрету скажу: каждый раз, когда ты проектируешь интерфейс компонента или библиотеки, ты создаешь internal DSL по обращению с этим компонентом или библиотекой.
Именно поэтому ты не можешь легко заменять библиотеки в своей программе: у каждой - свой язык и свой набор идиом на этом языке.
И когда говорят, что с такой-то библиотекой удобно работать - это значит, что она предоставляет пользователю выразительный internal DSL, который ведет к цели кратчайшим путем.
Так что тебе, как библиотекостроителю, нужно как раз изучать подходы к проектированию прикладных языков, а не отмахиваться от них.
>И когда говорят, что с такой-то библиотекой удобно работать - это значит, что она предоставляет пользователю выразительный internal DSL, который ведет к цели кратчайшим путем.
Ты упускаешь еще один момент: интеграция нескольких библиотек в одном фрагменте кода. С библиотеками ситуация достаточно простая -- это просто вызовы функций. Например:
SMTPClientSession session(...);
MailMessage msg;
msg.setSubject( subj );
for_each( recipients, [msg]( const Recipient & r ) { msg.addRecipient( r ) } );
session.send( msg );
С internal DSL-ями сложнее -- они имитируют синтаксис нового языка. Что-то типа:
SMTPClientSession.open session =>
session.mail
subject( subject )
recipient( recipients )
body( body )
send;
И вот тут встает вопрос: "а насколько удобно объединять в одном фрагменте разные DSL-и?" Что делать, если нужно вставить несколько получателей письма? Что делать, если нужно вставить нестандартный заголовок в письмо?
Скажем, с библиотекой я могу разбить подготовку письма на несколько независимых операций:
MailMessage msg;
setup_subject( msg );
setup_recipients( msg );
setup_headers( msg );
session.send( msg );
Смогу ли я так же переписать DSL-ный фрагмент?
А что, если я захочу разные параметры устанавливать в параллель:
MailMessage msg;
parallel_do(
[]() { setup_subject( msg ); },
[]() { setup_recipients( msg ); },
[]() { setup_headers( msg ); } );
session.send( msg );
Позволит ли мне это делать DSL?
Я сталкивался с такими вещами в Ruby и, немножко, в Scala. Проектировать DSL-и сложнее, чем библиотеки. И неудачно спроектированный DSL гораздо хуже, чем неудачно спроектированная библиотека.
ну, все нормальные библиотеки предоставляют оба интерфейса - как дсл-ный, так и обычный (например, Буст.Спирит).
В результатена дсл-ном варианте интерфейса легко пишутся собственно грамматики, а на обычном - когда ты какие-то куски генеришь кодом.
Ну и библиотеки - это не только вызовы функций, это и идиомы.
Например, однофазной/двухфазное создание объектов.
Или коды ошибок/исключения.
Или ручной диспетчер сообщений/колбеки.
И т.д.
>ну, все нормальные библиотеки предоставляют оба интерфейса - как дсл-ный, так и обычный
Ну, это пока их мало :)
Как только DSL-и начнут клепать направо и налево, так уровень нормальности серьезно снизится. Мы вот с тобой про reusing по соседству разговариваем и там есть фактор отсутствия времени на выделение и оформления reusable кода. Когда в прикладных проектах разработчики начнут штамповать для себя штучные прикладные DSL-и, вот тогда точно веселуха начнется.
>Ну и библиотеки - это не только вызовы функций, это и идиомы.
Например, однофазной/двухфазное создание объектов.
Или коды ошибок/исключения.
Или ручной диспетчер сообщений/колбеки.
Вот с DSL-ями все это будет гораздо хуже. Т.к. сейчас хотя бы собственные обертки над чужими библиотеками можно делать (например, добавлять исключения или прятать их за коды возврата). А в DSL-е как разработчик DSL-я решит, так и будет.
Не представляю я, как над кодом, задачей которого является производство побочных эффектов, можно построить интерфейс без побочных эффектов? Туп-туп. Буду признателен, если кто-нибудь сможет разъяснить мне это на пальцах или хотя бы выскажет идею на этот счет
Основная суть изоляция. Например в функциональном реактивном программировании есть переменные которые могут меняться внешними по отношению коду причинами, например переменная в которой содержатся координаты мышиного курсора, но произвольно менять эту переменную наш код не может, а вот написать кучу вполне чистых функций зависящих от этой переменной легко.
Отправить комментарий