воскресенье, 8 ноября 2009 г.

[comp.prog] Впечатления от интервью Джона Хьюза

На 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 комментариев:

jazzer комментирует...

> Как по мне, так DSL-и (особенно internal DSL-и) не есть хорошо.

Я тебе по секрету скажу: каждый раз, когда ты проектируешь интерфейс компонента или библиотеки, ты создаешь internal DSL по обращению с этим компонентом или библиотекой.

Именно поэтому ты не можешь легко заменять библиотеки в своей программе: у каждой - свой язык и свой набор идиом на этом языке.

И когда говорят, что с такой-то библиотекой удобно работать - это значит, что она предоставляет пользователю выразительный internal DSL, который ведет к цели кратчайшим путем.

Так что тебе, как библиотекостроителю, нужно как раз изучать подходы к проектированию прикладных языков, а не отмахиваться от них.

eao197 комментирует...

>И когда говорят, что с такой-то библиотекой удобно работать - это значит, что она предоставляет пользователю выразительный 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 гораздо хуже, чем неудачно спроектированная библиотека.

jazzer комментирует...

ну, все нормальные библиотеки предоставляют оба интерфейса - как дсл-ный, так и обычный (например, Буст.Спирит).
В результатена дсл-ном варианте интерфейса легко пишутся собственно грамматики, а на обычном - когда ты какие-то куски генеришь кодом.

Ну и библиотеки - это не только вызовы функций, это и идиомы.
Например, однофазной/двухфазное создание объектов.
Или коды ошибок/исключения.
Или ручной диспетчер сообщений/колбеки.
И т.д.

eao197 комментирует...

>ну, все нормальные библиотеки предоставляют оба интерфейса - как дсл-ный, так и обычный

Ну, это пока их мало :)
Как только DSL-и начнут клепать направо и налево, так уровень нормальности серьезно снизится. Мы вот с тобой про reusing по соседству разговариваем и там есть фактор отсутствия времени на выделение и оформления reusable кода. Когда в прикладных проектах разработчики начнут штамповать для себя штучные прикладные DSL-и, вот тогда точно веселуха начнется.

>Ну и библиотеки - это не только вызовы функций, это и идиомы.
Например, однофазной/двухфазное создание объектов.
Или коды ошибок/исключения.
Или ручной диспетчер сообщений/колбеки.


Вот с DSL-ями все это будет гораздо хуже. Т.к. сейчас хотя бы собственные обертки над чужими библиотеками можно делать (например, добавлять исключения или прятать их за коды возврата). А в DSL-е как разработчик DSL-я решит, так и будет.

Rustam комментирует...

Не представляю я, как над кодом, задачей которого является производство побочных эффектов, можно построить интерфейс без побочных эффектов? Туп-туп. Буду признателен, если кто-нибудь сможет разъяснить мне это на пальцах или хотя бы выскажет идею на этот счет

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