суббота, 23 сентября 2017 г.

Ловушки для создателя языка программирования

Практически каждый программист является создателем языков программирования. Каждый раз когда разработчик задумывается о том, почему в используемом языке чего-то не хватает или, наоборот, что-то мешается, он создаёт в своей голове образ изменённого языка, лучше соответствующего его желаниям. Такие образы могут содержать как дельные рационализаторские предложения, так и быть смесью неупорядоченных, возможно, противоречивых возможностей, заимствованных из других языков или желаний. За воплощение таких языков большинство разработчиков никогда не возьмётся, но, тем менее, такие упражнения вполне можно назвать созданием языка. Поэтому размышления о возможных ошибках при проектировании языка в контексте достижения корректности кода могут быть интересными многим, также позволяя лучше понять те или иные вопросы дизайна языка.

  1. Восприятие маленьких примеров.

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

    1. Если код показывает возможность ошибки, то создатель может пренебречь ею из-за очевидности в небольшом примере.
    2. Если пример показывает возможность оптимизации работы программиста или компьютера, что подаётся как преимущество, зачастую, не учитывается возможно малый объём подобного кода в реалистичном проекте. Это может приводить к преувеличению пользы от рассматриваемой возможности, которая, тем не менее, может отрицательно сказываться на общей простоте языка при большом совокупном объёме таких псевдо-улучшений.
    3. В небольшом примере не видны потребности, возникающие в большом объёме кода: возможность перекрёстного влияния с другим кодом, необходимость работы с габаритными конструкциями, вопросы длительного жизненного цикла и взаимодействия разных людей, а также влияние на совокупную сложность языка.

  2. Пренебрежение деталями.

    Увлекаясь великими идеями, которые способны спасти человечество в лице программистов от большинства бед, настоящих или надуманных, разработчики языков и им сочувствующие нередко забывают о важных деталях, которые словно червоточина способны разъесть великие идеи, как моль ковёр.

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

    Учёт корректности и обработка особых случаев - это одни из самых важных деталей, о которой часто забывают, и о которые разбиваются многие абстрактно великие идеи.

  3. Мода и маркетинг.

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

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

    Другой, на первый взгляд, парадоксальной особенностью, которую желательно учитывать, является то, что недостатки языка, не превышающие определённого предела, тоже могут выступать в качестве факторов, способствующих популярности. Программисты с удовольствием и неподдельным интересом делятся "интересными ошибками" друг с другом, обожают разгадывать головоломки кода, в том числе на собеседованиях(!), устраивают конкурсы по запутанному и неочевидному коду. Необходимость в преодолении высокого входного порога воспринимается как повод для гордости, а простота и очевидность, порой, высмеиваются.

  4. Развлечение, удовлетворение интереса.

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

  5. Увлечение красивыми концепциями.

    Многим нравится идея положить в основу языка некую центральную концепцию, вокруг которой построить всё остальное. Примерами такого подхода являются:

    1. Forth, строящийся вокруг операций со стеком.
    2. LISP, развивающий идею читаемого дерева "абстрактного" синтаксиса.
    3. Smalltalk, в котором всё строится на посылке сообщений объектам.
    Одной из главных плат за концепцию, как правило, является более сложное восприятие программ и сложность достижения целей, не вписывающихся в концепцию. Для целей языка защищённого программирования я не знаю иной концепции, которая бы им подошла, кроме опоры на естественнонаучный смысл процесса программирования.

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

  6. Забвение истории, и, наоборот, её буквальное понимание.

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

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

  7. Наследие.

    Заманчивой идеей для создания языка с позиции прагматиков, нацеленных на максимально быстрый результат, является тактика маленьких шагов. Заключается она в том, чтобы вместо формирования требований к языку сверху, сверяя их с возможностями воплощения, отталкиваться от того, что уже есть сейчас, пытаясь минимальными усилиями подогнать это под желаемое, постепенно подходя к цели без резких и болезненных переходов, максимально используя старые наработки. Примером может служить путь от С до Swift. Начавшись как библиотека и препроцессор для С, язык получил развитие в виде самостоятельного языка Objective-C, обратно совместимого с C и его проблемами, и после длительного эволюционного расширения вылившегося в язык Swift, который похож на Objective-C, лишённого необходимости быть совместимым с C и открытым для других новшеств.

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

  8. Гордыня.

    Многим программистам, как и другим людям, свойственно рассматривать себя как неповторимых личностей, которые лучше окружающего большинства. И чем более талантлив программист, тем больше у него поводов так считать. Такое мнение требует определённых подтверждений, в мире программирования это умение оперировать вещами, которые у других людей вызывают сложности. Поэтому при выборе возможностей для языка автор может отказаться считаться с потребностью в упрощении, даже если это не ведёт к ухудшению полезных характеристик языка, но может лишить его ореола элитности, открыв его для "быдлокодеров с кривыми руками и ошибками в ДНК". Также, многие программисты склонны преувеличивать собственные способности к борьбе с ошибками и отказываются от подстраховки со стороны языка в пользу возможностей, которые "не мешают полёту творческой мысли".

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

  9. Пренебрежение теорией.

  10. Проектирование сверху вниз, оторванное от анализа воплотимости

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

    При почти равных выходных возможностях по разному сформулированные языки могут отличаться по сложности воплощения на порядки.

Комментариев нет:

Отправить комментарий