Хабрахабр

Lua in Moscow 2019: интервью с Роберто Иерусалимским

Мы взяли у него интервью, в ходе которого задали и вопросы от читателей Хабра. Некоторое время назад наш московский офис посетил Роберто Иерусалимский, автор языка Lua. Если бы вы снова создавали Lua с нуля, какие три вещи вы изменили бы в этом языке? Наконец-то мы можем поделиться с вами всей записью разговора.
— Давайте для начала пофилософствуем.

Трудный вопрос. — Ого! Это не было каким-то одним большим решением. За созданием и разработкой языка стоит целая история. И программисты всё время на это жалуются, потому что сломалась совместимость. Были решения, о которых я сожалел и которые с годами смог исправить. А я размышляю лишь о каких-то небольших задачах. Мы поступали так несколько раз.

Думаете, это верный путь? — Глобальность по умолчанию (Global-by-default)?

Но этот путь очень труден для динамических языков. — Может быть. Вам нужны one-liner, print(sin(x)), а потом вам нужно объявить «print» и ещё «sin». Возможно, следует совсем отказаться от понятия «по умолчанию», но тогда будет тяжело использовать переменные.
Например, вам придётся как-то объявлять все стандартные библиотеки. Странно иметь объявления для таких коротеньких скриптов.

Локальность по умолчанию (Local-by-default) — это не решение, она вообще не существует. Всё, что крупнее, не должно иметь ничего по умолчанию, как мне кажется. Мы что-нибудь присваиваем, затем используем и снова присваиваем, и возникает совершенно необъяснимая ошибка. Она только для присваивания, а не для использования.

Думаю, какое-то объявление, возможно, опциональное… Мы много раз намеревались сделать какое-то глобально объявление. Возможно, глобальность по умолчанию не идеальна, но локальность по умолчанию точно не вариант. Но если мы будем слепо внедрять всё, о чём нас просят, ничего хорошего не выйдет

В конце концов, строгий режим (strict mode) — это разумный компромисс. (саркастически) Да, мы соберёмся внедрить глобальное объявление, добавим то-сё, пятое, десятое, вытащим другое, и в результате поймём, что финальное решение не удовлетворяет большинство программистов, и мы не добавим все возможности, о которых нас просят, и поэтому мы не внедряем ничего.

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

— Глобальность по умолчанию ещё удобна для маленьких конфигурационных файлов.

— Да, верно, для маленьких скриптов и тому подобного.

— И никаких компромиссов в данном случае?

В данном случае компромисс между маленьким скриптами и настоящими программами, что-то вроде того. — Всегда есть компромиссы.

Как мне представляется, вас вполне устраивает существующее положение вещей, верно? — И тут мы возвращаемся к первому большому вопросу: какие три вещи вы изменили бы, будь такая возможность?

Я об этом сожалею. — Ну, я бы не назвал это большим изменением, и всё же… Одним из неудачных решений, которое стало большим изменением, это nil в таблицах (nils in tables). C полгода-год назад я выпустил версию Lua, в которой были nil в таблицах. Я сделал этот хак… Вы знаете, о чём я говорю?

— Nil-значения?

Думаю, это называется nil в таблицах — то есть null. — Именно. Мы сделали грамматический хак, чтобы обеспечить совместимость.

— Зачем это понадобилось?

Мне говорят, что в массивах не может быть nil, поэтому массивы должны быть отделены от таблиц. — Я действительно убежден, что это целая проблема… Я думаю, что большинство проблем с nil в массивах исчезли бы, если бы мы могли иметь [nil в таблицах]… Точнее, проблема не с nil в массивах. Поэтому проблема заключается не в том, как мы представляем массивы, а в таблицах. Настоящая проблема в том, что мы не можем держать nil в таблицах! Так что об этом я очень сожалею, и многие люди не понимают, как всё могло бы измениться, если бы Lua позволял размещать nil в таблицах. Если бы мы могли иметь nil в таблицах, то имели бы nil в массивах без всего остального.

У нас есть своя реализация null, представляющая собой CDATA на null-указатель. — Могу я рассказать одну историю про Tarantool? Но обычно с этим возникают проблемы, потому что CDATA всегда преобразуется в «true». Мы используем её в тех случаях, когда нужно создавать в памяти пустые фрагменты для вставки аргументов позиционирования при удалённых вызовах, и так далее. И nil в массивах позволил бы решить много проблем.

Именно об этом я и говорю — это решило бы массу проблем для многих, но это ведет к ещё большей проблеме совместимости. — Да, я знаю. Но, может быть, однажды мы выпустим такую версию. У нас не хватает смелости выпустить версию, которая будет настолько несовместима, а затем разрушить сообщество и выпустить другую документацию для Lua 5, Lua 6 и т.д. Считаю, что так нужно было поступить с самого начала, и тогда мы бы говорили о тривиальном изменении в языке, если не считать совместимости. Это были бы очень большие перемены. А сегодня это поломает множество программ.

— Какие вы видите недостатки кроме вопроса совместимости?

Что-то вроде «клавиши Delete», потому что присвоение nil не удалит ключ, так что нужна будет примитивная операция для его настоящего удаления из таблицы. — Понадобятся две новые примитивные функции. И понадобится «test» для проверки, где именно есть отличие между nil и отсутствием данных.

— Вы анализировали влияние этих нововведений в практических реализациях?

Как я говорил, это незаметно сломало код. — Да, мы выпустили такую версию Lua. И намеренно сделано так, что если функция ничего не хочет вставлять, она возвращает nil. Некоторые применяют вызов функции table.insert(f(x)). Как и в любом другом языке, баг превратился в фичу, и люди ею пользуются. Так что вместо отдельной проверки «хочу ли я вставлять?» я вызываю table.insert, и если получаю nil, то знаю, что вставляться не будет. Но если фичу изменить, это сломает код.

Как nil, только void? — А что насчёт типа void?

Вы просто откладываете решение проблемы. — Ну нет, это кошмар. Это не решение. Если вы вводите один «костыль», то потом вам понадобится ещё один, и ещё, и ещё. Вот типичный пример: мы говорим — «избегайте nil в массивах, то есть дыр». Одна из проблем в том, что nil уже укоренился во многих местах языка. Такая конструкция уже сама подразумевает, что собой представляет nil… Например, если я хочу сделать список из результатов, возвращённых функцией, только для того, чтобы их все поймать. А потом пишем функцию, которая возвращает nil и что-то после него, и получаем ошибочный код.

— Для этого у вас есть хак 🙂

Однако библиотеки делают так… Как-то я думал об этом: возможно, библиотеки должны вместо nil возвращать false, хотя это сырой вариант, он решит лишь малую часть проблемы. — Совершенно верно, но вы не обязаны использовать хаки для решения таких простых и очевидных задач. В противном случае нам не стоит так часто использовать nil, как мне кажется. Настоящая проблема, как я говорил, в том, что нам нужно иметь nil в таблицах. Так что если вы создали void, эти функции всё равно будут возвращать nil, и мы не решим проблему, пока не создадим новый тип и функции не будут возвращать void вместо nil. В общем, непонятно.

А nil может работать, как и раньше. — С помощью void можно явно сказать, что ключ должен храниться в таблице, ключ со значением void.

Все эти функции в библиотеках должны возвращать void или nil. — Да, это я и имею в виду.

— Они могут также возвращать и nil, почему бы и нет?

— Потому что тогда для некоторых случаев мы не решим проблему невозможности захвата всех значений, которые возвращает функция.

— Но у нас не будет первого ключа, только второй.

— Нет, второго не будет, потому что подсчёт будет вестись неверно и в массиве появится дыра.

— То есть вы хотите сказать, нужен фальшивый метаметод?

Я мечтаю о подобном: — Да.

А затем я смогу сказать %x или #x, и таким образом узнаю количество возвращённых результатов. Нужно перехватить все результаты, возвращаемые функцией f(x). Так что создание void проблемы не решит, пока не будет очень строго правила, что функции никогда не возвращают nil. Вот как должен работать нормальный язык. Быть может, стоит от него отказаться. Но тогда зачем нам вообще nil?

Вроде «Lua Check на стероидах»? — Роберто, будет ли в Lua гораздо более сильная поддержка статического анализа? Вы говорили, что эта фича появится в версии 6. Конечно, я понимаю, это не решит всех проблем. И если в 5.х будет мощный инструмент статического анализа — если в него будут вложены человеко-часы, — то поможет ли это? 0, если вообще появится, верно?

Если вам нужен действительно мощный инструмент, используйте статически типизированный язык, вроде Haskell, или вообще какой-нибудь язык с зависимыми типами. — Нет, я считаю, что действительно мощный инструмент статического анализа называется… системой типов! Тогда у вас точно будет мощный инструмент для анализа.

— Но тогда у меня не будет Lua.

— Верно, Lua нужен для…

Мне нравится ваша картинка с жирафом про статические и динамические типы. — Неопределённости?

— Ага, это мой последний слайд из презентации.

(и почему нет)» на конференции Lua in Moscow 2019.
Последний слайд презентации для выступления Роберто Иерусалимского «Почему Lua?

Если я правильно понял, вы считаете, что Lua — это маленький удобный инструмент для решения не очень крупных задач. — Чтобы задать следующий вопрос, вернёмся к этой картинке.

Я всей душой верю в тесты. — Нет, я считаю, что вы можете решать какие-то большие задачи, но не подразумевающие статический анализ. Я выступал в Стокгольме по поводу тестовой комнаты, вы там были. Кстати, я не согласен с вами относительно покрытия, что мы не должны гнаться за покрытием… Я хочу сказать, я полностью согласен с мнением, что покрытие не обеспечивает полного тестирования, однако отсутствие покрытия вообще не позволяет тестировать. Что-то полностью сломалось в заголовочном файле Microsoft, C и C++. Я начал тестировать с [несколькими] багами — это самое странное, — один из них был широко известен, а остальные совершенно неизвестны. Я поискал в сети, но никто об этом либо не беспокоился, либо даже не замечал этого.

Мы преобразуем целую или дробную часть числа. К примеру, есть математическая функция modf(), которой нужно передать указатель на double-значение, потому что она возвращает два double. Затем появилась C 99, и эта функция теперь нужна для float-чисел. Эта функция давно являлась частью стандартной библиотеки. То есть первая функция подвергалась неявному преобразованию типов. А заголовочный файл Microsoft просто сохранял эту функцию и объявлял ещё одну в качестве макроса. Double преобразовывалось во float, а затем указатель на double преобразовывался в указатель на float!

— С этой картинкой что-то не так.

Если ты вызываешь эту функцию один раз, с любыми параметрами, и затем проверяешь результат, то он будет ошибочным, если только это не 0. — Это заголовочный файл из Visual C++ и Visual C 2007. Вы никогда не сможете использовать эту функцию. Любое другое значение не будет верным. А потом было много дискуссий по поводу тестирования… Просто однократно вызываешь функцию и проверяешь результат! Нулевое покрытие. У Apple был очень известный баг, что-то вроде "if… what… goto… ok". Эта проблема существовала долгое время, многие годы это никого не волновало. И потом много спорили о том, нужны ли правила, должны ли в стиле кода использоваться фигурные скобки, и так далее, и тому подобное. Кто-то вставил сюда другое выражение, а всё стало нормально. Никто этого не сделал… Никто не упомянул о том, что есть и много других «если».

— Там ещё и проблема с безопасностью, насколько я помню.

Ведь они тестируют только подтверждённые ситуации. — Совершенно верно. Это означает, что в приложении, проверяющем безопасность, нет ни одного теста, который бы проверял отказ в каких-то соединениях, или в чём там должно быть отказано. Они не тестируют всё подряд, потому что всё будет подтверждено. Ведь никто даже не тестировал то, что я подразумеваю под «покрытием». А все спорят о том, нужны ли скобки… Нужны тесты, как минимум тесты! Конечно, очень тяжело было бы обеспечить покрытие базовыми тестами, прогнать все строки кода. Это невероятно, люди даже не прогоняют базовые тесты. Я хочу призвать вас обратить внимание на те части программы, о которых вы забыли. Люди избегают даже базовых тестов, так что покрытие минимальное. Что-то вроде подсказки, как можно немного улучшить ваши тесты.

83%! — Знаете, какое покрытие тестами в Tarantool? А какое покрытие в Lua?

Сколько у вас строк кода? — Около 99,6 %. Это огромное количество. Миллион, сотни тысяч? Вы вообще их не исполняли. 1 % от ста тысяч — это тысяча строк кода, которые никогда не тестировались. Ваши пользователи ничего не тестируют.

— То есть около 17 % функций Tarantool сейчас не используются?

Даже используя статический язык, — не наподобие Haskell, а что-то вроде Coq, — то пока у вас не будет системы проверки, вы будете менять то на это. — Вряд ли вы захотите снова вернуться к тому, о чём мы говорили… Я думаю, одна из проблем динамических языков (да и статических тоже) заключается в том, что люди не тестируют свой код. А если они у вас есть, вы сможете выявить глобальные проблемы, опечатки в именах и так далее, всевозможные ошибки. И никакой инструмент статического анализа не сможет выловить эти ошибки, поэтому вам нужны тесты. Иногда отлаживать будет сложно, иного просто — зависит от языка и вида бага. Вам обязательно нужны тесты. С другой стороны, они не гарантируют отсутствие ошибок, но с ними я чувствую себя куда увереннее. Но суть в том, что никакой инструмент статического анализа не заменит тестов.

Я, как разработчик, хочу тестировать какие-то локальные функции, которыми позднее могу воспользоваться. — У нас вопрос о тестировании Lua-модулей. Вопрос: нужно покрытие на уровне 99 %, но количество функциональных ситуаций, которые должен сгенерировать модуль для API, гораздо меньше количества функциональности, которую он поддерживает внутренне.

— Простите, почему же?

— Есть функциональность, которая недоступна для публичных интерфейсов.

— Её там быть не должно, просто удалите этот код.

— Просто удалить?

Было какое-то покрытие кода, я не мог попасть сюда, туда или туда, я решил, что это невозможно и просто удалил код. — Да, я так иногда делаю в Lua. Если какие-то ситуации невозможны, просто пишите в комментарии, почему это невозможно. Редко, но такое бывает. Нужно писать публичные API с ошибочными входными данными, это важно для тестов. Если не можете из публичного API попасть внутрь функции, её быть не должно.

Меньше сложность — выше стабильность и удобство сопровождения. — Удалять код хорошо, это уменьшает сложность. Не усложняйте.

Если что-то нельзя протестировать, оно не существует. — Да, в экстремальном программировании существует такое правило.

Какие парадигмы, или функциональные особенности, или части каких языков вам нравятся? — Какими языками вы вдохновлялись при создании Lua?

Поэтому, когда вы спросили меня о повторном создании языка, я ответил, что за этим стоит целая история. — Я проектировал Lua для очень узкого назначения, это не был академический проект. У меня была проблема: здесь нужен язык конфигурирования для геологов и инженеров, он должен быть маленьким и с простым интерфейсом, чтобы они могли им пользоваться. Я не начинал разрабатывать Lua с «создам язык, который мне нравится, или который хочу использовать, или который всем нужен». Такова была задача. Поэтому API всегда был составной частью языка. Что касается вдохновения, в то время я был знаком примерно с десятью разными языками.

— Мне интересно, какие языки вы хотели включить в Lua.

Наибольшее влияние оказал синтаксис языка Modula, хотя трудно сказать, ведь языков так много. — Я позаимствовал идеи из многих языков, всё, что подходило для решения моей задачи. Конечно, из Scheme и Lisp… Я неравнодушен к Lisp с тех пор, как стал программировать. Что-то взял из AWK.

— И в Lua до сих пор нет макросов!

Наверное, первым языком был Fortran… нет, моим первым языком был ассемблер, а затем Fortran. — Да, синтаксис получился совсем другим. Много программировал на Smalltalk и SNOBOL. Я изучал, но никогда не пользовался CLU. Многое позаимствовал из Pascal и С. Также изучал, но не использовал Icon, тоже очень интересный язык. Я начал работу в 1991-м и выпустил Lua в 1993-м. Когда я создавал Lua, C++ был для меня уже слишком сложен, это было ещё до шаблонов и прочего.

Мне казалось, его синтаксис будет аналогичен С, потому что Lua интегрирован в него. — Развалился СССР и вы начали создавать Lua 🙂 Вам не нравились точки с запятой и объекты, когда вы приступили к Lua? Однако...

— Я считаю, что синтаксисы должны различаться, тогда вы не запутаетесь, ведь это два разных языка.

Мой ответ был слишком длинным. Это действительно забавно, и связано с ответом про начинающиеся с единицы массивы, который вы не дали мне высказать [на конференции].

Не все языки были похожи на С. Когда был выпущен Lua, мир был другим. 0. Ещё не было Java и JavaScript, Python был младенцем и не перевалил за версию 1. Так что не все языки были похожи на С, его синтаксис был одним из многих.

Забавно, что большинство этого не осознаёт. То же самое и с массивами. У массивов, начинающихся с 0 и с 1, есть свои достоинства.

Их создатели вдохновлялись С. Именно благодаря С в большинстве популярных языков сегодня используются массивы, начинающиеся с 0. Вы не можете сказать, что С индексирует массивы с нуля, потому что операции индексирования там не существует. И забавно, что в С нет индексации. А раз это смещение, оно должно быть нулевым не потому, что так проще вычислять, а потому, что это естественно. В этом языке есть арифметические операции с указателями, так что ноль в С — это не индекс, а смещение (offset).

Java, JavaScript и многие другие — ни в одном нет таких операций. И все языки, скопировавшие С, имеют индексы и не имеют арифметических операций с указателями. Этот ноль они вставили безо всякой причины, прямо «культ карго». Их авторы просто скопировали 0, но это совершенно другое.

Но тогда должны быть С-программисты, которые хотят, чтобы код был на С, а не каком-то другом языке, который выглядит как С, но это не С. — Вы говорите, что если язык встроен в С, то логично сделать его синтаксис похожим на С. Почему? То есть вы не предполагали, что пользователи Lua будут ежедневно писать на С?

— Кто ежедневно пишет на С?

— Системные программисты.

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

— Но ведь Lua реализован на C.

Тут вам и переполнения буфера, и целочисленные переполнения, приводящие к переполнению буфера… Попробуйте написать программу на С, в которой не будет проблем с вычислениями, если пользователь введёт куда-нибудь любое число и всё будет проверено. — Да, и так мы узнали, насколько трудно программировать на С. А тут ещё проблемы с портированием: иногда на одном процессоре работает, а на другом… Безумие.

Как узнать, не приводит ли ваша программа на С к переполнению стека? К примеру, совсем недавно у нас была проблема. Я имеют в виду глубину стека, а не его переполнение из-за вашего вмешательства… Сколько вызовов можно сделать в программе на С?

— Зависит от размера стека.

Что нам говорит документация? — Верно. Если вы пишете на C и делаете функцию, которая вызывает другую функцию, которая вызывает другую функцию… то сколько вызовов можно сделать?

— 16 тысяч?

— Могу ошибаться, но в документации ничего об этом не говорится.

— Думаю, это потому, что всё зависит от размера.

Они могут быть огромными, с массивами в аргументах, с автоматическим удалением в рамках функции… В документации ничего не сказано, и никак не проверить, является ли вызов корректным. — Конечно, это зависит от размера каждой функции. Корректная с точки зрения стандарта, но не корректная, потому что падает. Даже с трёхступенчатыми вызовами у вас может возникнуть проблема — программа корректная, но при этом падает. Так что программировать на С очень сложно, поскольку здесь много нюансов… Ещё хороший пример: какой будет результат при вычитании двух указателей?

— Но C++ поддерживает разные типы.

— Нет, в C++ та же проблема.

ptrdiff_t? — Какой тип у объявления?

Обычно, если слово занимает в памяти стандартный объём и в этом пространстве вычитаются два указателя, вы не можете представить все размеры с помощью типа со знаком. — ptrdiff_t — это тип со знаком (signed type). Что об этом говорится в документации?

Иначе вы получите неопределённое поведение. Вычитая два указателя, если ответ помещается в diff указателя, тогда это будет ответом. Никак. А как узнать, помещается ли ответ? Хотя если вы индексируете элементы по 2 байта и больше, то индекс в знаковый тип поместится и всё будет хорошо. Обычно, когда вы вычитаете указатели, вы должны быть готовы к проблемам.

Тогда вы выполняете арифметические операции с указателями с риском, что строковое значение будет больше половины памяти. Проблема возникает лишь тогда, когда вы ссылаетесь на байты или символы. В этом случае не получится просто вычислить размер и сохранить его в типе diff указателя, потому что он ошибочен.

Вот что я имею в виду, говоря о безопасной программе на С или С++.

— Вы рассматривали возможность реализации Lua на другом языке?

Возможно, полезно что-то позаимствовать из С++, но… даже сегодня я не вижу других кандидатов среди языков. — Когда мы начинали, я рассматривал C++, но отказался от него из-за сложности, я не мог выучить весь язык.

— Можете объяснить, почему?

Я могу лишь сказать, чем мне не нравятся другие языки. — Потому что у меня нет альтернатив. Чтобы объяснить своё мнение, нужно сравнить его с другими языками. Я не хочу сказать, что С идеален или даже хорош, но это лучший вариант.

— Что насчёт JVM?

Да ладно, она не подходит для половины существующего железа… Портируемость — главный недостаток, но ещё и проблемы с производительностью. — Ох, JVM. NET, но разница невелика. У JVM она выше, чем у . К примеру, вы не можете управлять сборщиком мусора. В JVM нельзя сделать многое из того, что делает Lua. JVM потребляет кучу памяти. Придётся использовать сборщик JVM, потому что нельзя поверх него реализовать другой сборщик. И портируемость является проблемой не потому, что JVM не была портирована, а потому что её нельзя портировать. Когда запускается любая Java-программа, она сжирает около 10 Мб.

— А что насчёт модификаций JVM, например, Mobile JVM?

Какая-то микроверсия Java, а не Java. — Это шутка, а не JVM.

Могут ли они стать основой для Lua, если бы вы сегодня создавали этот язык? — А другие статические языки, вроде Go или Oberon?

Oberon может быть кандидатом, но у этого языка есть большие странности. — Oberon… возможно… В Go есть свой сборщик мусора, а runtime-среда слишком велика для Lua. Ага, думаю, они перенесли const из Pascal в Oberon. Например, почти полное отсутствие констант, если я не путаю. На нём была написана невероятная операционная система.
Помню, в 1994-м я видел демонстрации Oberon и Self. У меня была книга об этом языке, он мне нравился. Это очень интересный динамический язык с JIT-компиляторами и прочим. Знаете про Self? Когда что-нибудь запускаешь, оно сначала — раз! Self был очень умным языком, авторы заимствовали ряд методик из мультипликации, чтобы замаскировать медлительность операций. Реализовано было очень хорошо, они с помощью этих методик эмулировали движение… — немного уменьшается, а потом раскрывается с какими-то эффектами.

В Oberon кликаешь, и всё немедленно работает, система была очень быстрой. А затем я посмотрел демонстрацию Oberon, который работал на железе, которое было примерно в 10 раз слабее железа для Self, очень старый компьютер.

Но для меня этот язык слишком минималистичен, авторы убрали константы и различные типы.

— Haskell?

— Я не знаю Haskell, и не знаю, как на нём реализовать Lua.

— А как вы относитесь к Python, R или Julia с точки зрения основы для новой реализации Lua?

— Думаю, у каждого из этих языков есть своя ниша.

Он очень сильно завязан на эту сферу, и в этом его сила. R хорош для статистики.

Кажется, я упоминал об этом на выступлении. Python хорош, но лично у меня с ним проблемы. Если не знаешь весь язык или не используешь его, это вводит в заблуждение.

Все сначала довольны, а потом просят сделать графическое приложение, и нам требуется какая-нибудь графическая библиотека. Мы используем Python на наших курсах, учим основам программирования, показываем циклы и целые числа. Он создаёт иллюзию лёгкости использования и предлагает библиотеки для любых нужд, но тут либо просто, либо «всё включено». А почти со всеми графическими библиотеками вы получаете API… Ну, я плохо знаю Python, это очень продвинутый язык.

Кучу библиотек. И когда начинаешь писать на этом языке, пошло-поехало: ой, нужно изучить ООП, наследования, ещё что-то. Вызовы функций, стандартные типы и так далее. Такое чувство, что авторы с гордостью демонстрируют в API более продвинутые возможности языка, просто чтобы что-нибудь показать. У вас есть такой объект, а если вы хотите другое, то вам нужно создать ещё один объект…

Вы сопоставляете, объект возвращает результат, применительно к нему вы вызываете метод, чтобы получить настоящий результат сопоставления. Вы можете сделать что-то простое, но обычно это не стандартный поиск по шаблону (pattern matching). Иногда есть способ проще, но он не очевиден, многие им не пользуются.

Я не мог использовать Lua, потому что у него совершенно иной синтаксис. Другой пример: я преподавал курс о поиске по шаблону и хотел использовать синтаксис наподобие Perl. Но в этом языке есть лишь несколько прямых функций для базовых вещей, а для всего посложнее нужно знать объекты, методы и прочее. Думал, Python отлично подойдёт. Мне же нужно было просто что-то сделать и получить результат.

— И чем же вы в конце концов воспользовались?

Но даже Perl гораздо проще: вы сопоставляете и получаете результат в виде $1, $2, $3. — В тот раз я объяснял на примере Python. Однако у меня не хватает смелости использовать Perl, так что…

— Я два года писал на Python, пока не заметил, что в нём есть декораторы (замечание Ярослава Дынникова из команды Tarantool).

Python создаёт иллюзию простоты, но он весьма сложен. — Ага, и когда хочешь использовать библиотеку, то нужно изучить её, а ты не понимаешь API, вот это всё.

Вы можете получить прекрасные результаты, но нужно очень хорошо понимать, что там происходит. Я мало знаю про Julia, но она напоминает мне про LuaJIT, с той точки зрения, что иногда выглядит как предмет гордости программиста. Нет, вы пишете код, иногда результат хороший, а иногда ужасный. Не будет так, что вы пишете код и получаете хороший результат. Вы просматриваете весь код и понимаете: а, вот это не оптимизировано из-за этого. И во втором случае у вас под рукой много хороших инструментов, отображающих сгенерированный код на промежуточном языке, почти ассемблере. Это проблема программистов, они любят играть, и иногда любят что-то за сложность, а не за простоту.

Не помню точно, там что-то было про матричное умножение. Я мало знаю про Julia, но однажды слушал выступление об этом языке: смотрите, какой классный язык, мы написали программу и она идеальна. Только в сто раз медленнее. А вот идеальные числа с плавающей запятой, а вот идеальные double, а потом мы вставили комплексные [числа]… это была трагедия.

Смотрите, как эффективно». (саркастически) «Смотрите, как классно, у нас есть инструмент, мы можем видеть весь ассемблер, а затем пойти и поменять это, это и это. Ага, вижу, я могу писать прямо на ассемблере.

Я мало изучал R, и немного писал на Python. Но это лишь одно выступление.

— Что думаете про Erlang?

У него есть ряд очень удачных применений, интересно реализована отказоустойчивость. — Это забавный язык. А у Erlang есть огромное скрытое состояние сообщений, которые отправлены, но ещё не получены. Но авторы утверждают, что это функциональный язык, однако такие языки не должны иметь состояний, в том-то и идея. Поэтому каждый небольшой процесс полностью функциональный, а программа целиком абсолютно не функциональная.

Сообщения, которые являются реальным состоянием вашей системы. Этот бардак со скрытыми данными гораздо хуже глобальных переменных, потому что в последнем случае вы бы их вывели на экран. Все эти сообщения, отправленные в разные места. Каково её состояние в каждый момент времени? Это полностью противоречит парадигме функционального языка.

О чём тогда врёт Lua? — Значит, Erlang врёт про свою функциональность, а Python врёт про свою простоту.

Lua всё ещё меньше большинства языков, но если вам нужен действительно маленький язык, то Lua окажется больше, чем вы ожидаете. — О том, что она мала.

— А какой язык тогда маленький?

— Forth, он мне нравится.

— В нём есть место для уменьшенной версии Lua?

Мне нравятся таблицы, но они не так уж малы. — Может быть, но это будет трудно. Синтаксис будет как в Lua, мы будем называть это Lua, но это будет уже другой язык. Если хотите представить что-нибудь маленькое, то таблицы вам не подойдут.

Можете называть её Java, но разве в ней есть многопоточность? Это будет как микроверсия Java. Есть рефлексия (reflection)? Нет. Так зачем её использовать? Нет. Это другой язык, он проще в изучении, если вы уже знаете Java, но это не она сама. У неё синтаксис Java, такая же система типов, но это совсем не Java.

Если хотите сделать маленький язык, который выглядит как Lua, только без таблиц он не… Возможно, ради миниатюризации придётся объявлять таблицы, словно FFI.

— Существуют ли уменьшенные адаптации Lua?

— Наверное, не знаю.

Вы можете это с ним сделать? — Готов ли Lua к чисто функциональному программированию?

Это не особенно эффективно, в этом преуспел только Haskell. — Конечно, вы можете так программировать. Если начнёте использовать монады и подобные конструкции, создавать новые функции, компоновать их… Вы можете это делать в Lua, будет работать, но чтобы получилось хорошо, придётся реализовать методики, отличные от тех, что применяются в нормальных императивных языках.

— Кстати, есть библиотека для обеспечения функционального программирования на Lua.

С её помощью многое можно сделать. — Да, она здравая и рабочая, если вам нужна производительность. Мне нравится функциональное программирование, я постоянно его практикую.

Lua для этого хорошо подходит? — Хочу спросить о сборщике мусора, потому что у нас только изменяемые объекты и нужно эффективно их использовать.

— Думаю, новая инкарнация сборщика будет полезна, но повторюсь…

Сборщик, который работает с молодыми объектами? — «Молодые умирают молодыми»?

Хотя, как я уже говорил, даже со стандартным сборщиком производительность будет не оптимальной, но допустимой. — Да, верно. Для большинства действий вам даже не понадобится высокая производительность, если вы пишете серверы или выполняете большие операции.

— Какие задачи из функционального программирования вы делаете на Lua?

Я пишу книгу в своём формате, и у меня есть форматировщик, который преобразует текст в LaTex или DocBook. — Простой пример. Вместо обратного слеша используется @, имя макроса и один аргумент в фигурных скобках. Он полностью функциональный, в нём выполняется большой поиск по шаблону… Он слегка похож на LaTex, но гораздо унифицированней. Инструмент полностью соответствует парадигме функционального программирования, в нём лишь функции поверх функций, поверх функций, и последняя функция выдаёт большой результат. Gsub распознаёт такие данные, а затем вызывает функцию, которая что-то делает и что-то возвращает.

— Почему вы не программируете на LaTeX?

Во-первых, если материала много, то получается слишком сложно и трудно. — Чистом LaTeX? Например, хочу вставить в текст кусок inline-кода. Есть несколько вещей, которые я не знаю, как реализовать в LaTex. А пробелы — это всегда неправильно. Ещё там есть стандартный оператор /, который даёт фиксированный пробел. А фиксированные пробелы иногда выглядят слишком большими, иногда слишком маленькими. Все настоящие пробелы переменные, это зависит от выравнивания строки, которая расширяется в одних местах и сжимается в других, в зависимости от разных критериев. Это тоже зависит от того, как вы пишете код.

— И всё же вы преобразуете свой формат в LaTeX?

Я пишу свой оператор, а затем он изменяется и становится не оператором, а данными. — Да, но после большой предварительной обработки. Если это оператор, то без пробелов он сжимается, а с пробелами становится слишком широким. Например, когда я пишу 3+1, то вставляю очень маленькие пробелы. Они очень маленькие, но если нужно настроить отображение, могут стать немного больше. Поэтому я выполняю предварительную обработку, вставляю переменные пробелы. Всё это делает одна функция. Однако, если после 1 я пишу «and», то потом вставляю большой пробел. Это небольшой пример, есть и другие.

— У вас есть исходный код?

Программа называется 2html. — Да, на git. Я написал её для книги, но есть ещё одна, для руководства. Текущая версия генерирует только HTML… Извините, она довольно неаккуратная. А главная проблема в том, что там нет TeX. Правда, она сложнее и не предназначена для публики, я не могу её раскрывать. Почти невозможно обработать TeX-файлы без самого TeX.

— Это машинный код?

В смысле, его может прочесть TeX. — Нет, не машинный. Так что мой форматировщик гораздо более унифицирован и умеет генерировать в формате DocBook, иногда он мне нужен. Его так сложно тестировать, там столько странных правил. Это началось, когда я заключил договор на написание книги.

— То есть с помощью 2html вы генерируете DocBook?

— Да, напрямую генерирую DocBook.

— Роберто, большое спасибо за интервью, ждем вас в следующем году!

— Спасибо вам за гостеприимство и за вопросы.

Если у вас остались какие-то вопросы, то вы можете задать их в Lua Mailing List.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»