Привет, Хабр!

Предлагаю вашему вниманию перевод статьи «Three Paradigms» автора Robert C. Martin (Uncle Bob).

image

За последние 40 лет технологии аппаратного обеспечения увеличили вычислительную мощность наших устройств более чем на двадцать порядков. Теперь мы играем в Angry Birds на наших телефонах, которые обладают вычислительной мощностью суперкомпьютера 70-х годов прошлого века с фреоновым охлаждением.

Но за те же 40 лет технологии программного обеспечения практически не изменились. В конце концов, мы все так же применяем операторы if, while loops и операторы присваивания, которые мы использовали еще в 60-х годах. Если бы я взял программиста из 1960 года и привел его сюда, чтобы он посидел за моим ноутбуком и написал код — ему понадобилось бы 24 часа, чтобы оправиться от шока, но он смог бы написать этот код. Принципы не так сильно изменились.

В процессе написания программ изменились три вещи. Я говорю не об оборудовании, не о скорости компьютера и не о невероятных инструментах, которые у нас есть. Я имею в виду сам код. Три вещи изменились в коде. Можно их назвать парадигмами. И все они были «открыты» за одно десятилетие более 40 лет назад.

* 1968 — Структурное программирование. Эдсгер Дейкстра написал свою статью: «О вреде оператора Go To» и ряд других документов и статей, в которых предлагается отказаться от использования необузданного подхода Go To, заменив его такими средствами, как if/then/else и while loops.

* 1966 — Объектно-ориентированное программирование. Оле-Йохан Даль и Кристен Нюгор, изучая язык Алгол, «Открывают» объекты и создают первый объектно-ориентированный язык-Симула-67. Несмотря на то, что это достижение имеет множество перспектив, оно не принесло никаких новых возможностей в наш код. Кроме того, оно удалило одну. Ведь с появлением полиморфизма необходимость в указателях на функции отпала, а на самом деле исчезла.

* 1957 — Функциональное программирование. Джон Маккарти создает Lisp-первый функциональный язык. Lisp был основан на лямбда-исчислении, созданном Алонзо Чёрчем в 30-х годах. Хотя существует много перспектив функционального программирования, однако во всех функциональных программах существует одно огромное ограничение. Они не используют присваивание.

Три парадигмы. Три ограничения. Структурное программирование устанавливает правила по прямой передаче контроля. Объектно-ориентированное программирование вводит правила по косвенной передаче контроля. Функциональное программирование вводит ограничения при присваивании. Все эти парадигмы отняли что-то. Ни одна из них не добавила никаких новых возможностей. Каждая из них повысила требования и уменьшила возможности.

Можем ли мы создать другую парадигму? Есть что-нибудь еще, что можно убрать?

За 40 лет не было новой парадигмы, так что, возможно, это хороший признак того, что больше нечего искать.

Должны ли мы использовать все эти парадигмы, или мы можем выбирать?

Со временем мы решили внедрить их. Внедрение первого структурированного программирования стало возможным благодаря отмене принципа Go To (как рекомендовал Дейкстра в своей статье). ООП была успешно внедрена путем замены указателей функциями в наших современных языках с помощью полиморфизма (например, Java, C#, Ruby). Поэтому, по крайней мере, для этих двух, ответом на данный вопрос является то, что мы ОБЯЗАНЫ использовать их. Все другие варианты были исключены или, по крайней мере, строго ограничены.

А как же функциональное программирование? Должны ли мы использовать языки, не имеющие оператора присваивания? Наверное, да! Мы уже занимаемся написанием кода, который должен хорошо работать на нескольких ядрах, и эти ядра размножаются, как кролики. Мой ноутбук имеет 4 ядра. У моего следующего, скорее всего, будет 8. Тот, что после 16. Как вы собираетесь писать надежный код с 4096 процессорами, борющимися за право доступа к шине?
Мы едва ли сможем заставить работать два параллельных потока, не говоря уже о 2^n процессорах.

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

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

Комментарии (31)


  1. DmitriyTitov
    12.11.2019 22:21
    +2

    За последние 40 лет компьютерные технологии увеличили вычислительную мощность наших устройств более чем на двадцать процентов.


    Если речь про утюги — то ОК, если про вычислители — то Боб такого ляпнуть не мог.


    1. Porohovnik
      12.11.2019 22:43
      +1

      Ну согласитесь в 1000раз это же больше 20%)


  1. Dorogonov_DA
    12.11.2019 22:21
    +2

    За последние 40 лет компьютерные технологии увеличили вычислительную мощность наших устройств более чем на двадцать процентов.


    У Вас может и на 20 процентов, а в оригинаольной статье сказано про 20 порядков.

    In the last 40 years computer hardware technology has increased the computing power of our machines by well over twenty orders of magnitude.


    И, кстати, сказано именно про аппаратную часть, что в переводе не отражено. А в следующем абзаце становится понятно, почему этот акцент был сделан:
    Но за те же 40 лет технологии программного обеспечения практически не изменились.


    1. Simple219 Автор
      13.11.2019 05:55

      Здравствуйте, описки/ошибки, указанные Вами, исправил.


  1. math_coder
    12.11.2019 22:29

    Есть ещё аспектно-ориентированное программирование во всём его многообразии, включая дженерики.


    И ещё, пожалуй, можно добавить Entity-Component-System.


  1. LibrarianOok
    12.11.2019 22:30

    Можно, например, вернуть Goto, как сделано в Lua, и перестать его бояться только лишь потому, что «ну, Дейкстра же сказал, что нильзя».


    1. playermet
      13.11.2019 15:10

      Инструкция goto о которой говорил Дейкстра, и goto в современных языках (в т.ч. Lua) — это немного разные goto с разными свойствами. Например, goto в Lua не может передать управление в тело другой функции, или в область видимости локальной переменной, недоступной до перехода.

      И даже так, круг применения goto крайне ограничен, и в большинстве случаев заменим. Настолько, что он ни разу не пригодился мне за 8 лет активной работы с Lua.


      1. LibrarianOok
        13.11.2019 15:16

        Именно так. Но

        «это же одно и то же.»
        image


        1. playermet
          13.11.2019 16:14

          Это совсем не одно и то же, если только вы не решите полностью отказаться от использования функций и объявите все переменные в самом начале программы. Почитайте Clean Architecture Роберта Мартина, в четвертой главе он подробно разъясняет, откуда выросли претензии к goto.


          1. LibrarianOok
            13.11.2019 16:18

            Я-то знаю, (там картинка пирата из широко известной в узких кругах шутки про «одно и то же» ) но как убедить в этом остальных?


            1. playermet
              14.11.2019 13:20
              +1

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

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


              1. LibrarianOok
                14.11.2019 13:59

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


                1. playermet
                  15.11.2019 14:02

                  Об аналогиях вообще, и с микроволновкой — в частности, следует помнить, что они ложные.
                  И вот вы сами уже возводите что-то в абсолют, причем по причине, которая проиллюстрирована самой аналогией с микроволновкой — «есть случаи неверного использования, значит мы запретим использование в принципе». Кстати, в чем я проглядел ошибку в аналогии с микроволновкой?


  1. virtual_hack2root
    12.11.2019 22:43

    20 поряков — это литературная гипербола, в этом смысле неважен порядок, а только направление. 10^20 это очень большое число, на самом деле точное чисто порядков можно вычислить, поделив общее число транзисторов первого кристалла архитектуры 80386 и текущего процессора Intel (однокристального). Данные в Winkpedia. Тут просто ощущения автора оригинального поста. И кстати, в настоящее время, чтобы увеличить производительность на 20 поцентов, как раз нужно уменьшить техпроцесс в два раза.


    1. Victor_koly
      13.11.2019 11:08

      Теоретически, 20 порядков может получиться при сравнении самого крутого современного суперкомпа в производительности с плавающей запятой. В древние времена операция умножения занимала несколько больше тактов. Если сейчас 150*10^15 PFlops, то на 20 порядков меньше будет 0.09 операций с п.з. в минуту.


  1. Daddy_Cool
    12.11.2019 22:48
    +5

    Краткое содержание статьи:
    ФП это хорошо.
    Полное содержание статьи:
    СП, ООП и ФП наложили ограничения. Можно ли еще что-то ограничить, чтобы все же оставалась возможность программировать? Видимо нет, но все же ФП это хорошо. Мы еще поговорим об этом позже.


  1. MaM
    12.11.2019 23:24
    +1

    Ну действительно всего 3 en.wikipedia.org/wiki/Programming_paradigm
    Какой то принцип Goto. Поймите уже наконец Дейкстра написал свою статью когда все писали на форсе и «иди туда» был вездесущ. Код был похож на вывод дизасма огромной функции с кучей стрелок, для goto есть свои применения и они будут, потому что это удачная концепция. А так можно и макрошаблонами устроить армагеддон и классами устроить ад. «была успешно внедрена путем замены функций указателями в наших современных языках с помощью полиморфизма» — ничего не понял но уверен что в любом случае автор не прав, полиморфизм во первых бывает статическим, во вторых то ООП что появилось ближе к акторам чем к тому что мы видим. «мы ОБЯЗАНЫ использовать их» — кому обязанны, трейд не раскрыт но думаю настало время удалить линукс — ооп то незавезли.


    1. ak394078
      13.11.2019 01:16
      -1

      ничего не понял но уверен что в любом случае автор не прав

      Вся суть хабра.


    1. saboteur_kiev
      13.11.2019 02:13
      +1

      тем более, что на ассемблере jmp никуда не девался, и отлично работает до сих пор, просто в силу отсутствия таких инcтрукций ассемблера, как begin/end (do/done, {/}, etc)


      1. BD9
        13.11.2019 04:08

        В ассемблере есть begin и end.


        1. VolCh
          13.11.2019 07:33

          Ассемблер бывают разные


        1. saboteur_kiev
          13.11.2019 15:53

          [Зануда моде] Окей, в инструкциях известных мне процессоров, в которые «компилируется» текст на ассемблере, конструкций для начала и конца блока — нет


    1. LibrarianOok
      13.11.2019 10:14

      Аминь! Когда я читал третье издание PiL и параллельно писал игрушку про программирование на стеке, был просто в восторге от красоты и простоты кода, который получается с использованием goto. Потом пришлось этот фрагмент переписать на while ради совместимости с Lua 5.1. Я до сих пор не вполне понимаю, как этот кусок работает теперь.


  1. megahertz
    13.11.2019 07:07

    Нельзя просто так взять и прочитать отдельную статью дядюшки Боба в отрыве от его остальных работ. На это есть несколько причин:


    • Роберт зачастую преподносит идею как единственно верную, такой у него стиль изложения. Но это лишь для упрощения повествования. Время от времени автор отмечает, что идея важна, но нужно думать своей головой.
    • Эта статья, по сути, заготовка к Clean Architecture и может выглядеть достаточно спорно в отрыве от этого контекста.
    • Пример с SOLID показателен. После прочтения Clean Architecture разными людьми было написано множество статей в виде краткого пересказа главы о SOLID. Из-за того что материал вырван из контекста, у многих складывается искаженное представление о том что хотел донести Роберт.


  1. epishman
    13.11.2019 08:14

    А как же асинхронное программирование, ведь это даже круче чем goto )


    1. sergey-gornostaev
      14.11.2019 09:26
      +1

      Немного перефразировав Алана Кокса, смею заявить, что асинхронное программирование может стать проблемой только для тех, кто не умеет программировать конечные автоматы.


      1. epishman
        14.11.2019 11:10

        Современный асинхрон это уже stackfull + streams, простой стейтмашиной не отделаться…


        1. sergey-gornostaev
          14.11.2019 18:20

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


  1. apapacy
    13.11.2019 09:59

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


    1. vassabi
      13.11.2019 17:18

      Если вам нравится Пролог, то можете еще глянуть на ru.wikipedia.org/wiki/Erlang


  1. Gryphon88
    13.11.2019 16:30

    Оффтоп: а не подскажете инструмент, который показывал бы локальность данных, а в идеале сам бы разносил независимые по данным части программы в разные потоки (да, я одновременно ленивый и наивный)? Функциональные языки это, конечно, хорошо, но иногда приходится пытаться распараллелить программу на С или плюсах.