?

Log in

О глубокой философии программирования (серьезный разговор) - ru_declarative [entries|archive|friends|userinfo]
ru_declarative

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

О глубокой философии программирования (серьезный разговор) [Feb. 18th, 2008|03:17 am]
ru_declarative

ru_declarative

[anonym_mouse]
.
Есть на Интернете программист-блоггер, 7 лет проработавший в Амазоне. Зовут его Стив Егге, и славен он тем, что в блоге публикует свои "rants" на компьютерные темы. Каждая запись - страниц 5 убористого текста. Многое разумно (ему 39 лет и он достаточно много программировал), но хотя бы из-за объема, многое противоречит себе же. Стив Егге - прототипический п..бол.

И однако одна из записей поразила меня глубоким проникновением в тему. Я давно о ней размышлял, именно в таких, грамматических, терминах. Представляю вам где-то 2/3 его записи в сокращенном несколько виде.


Steve Yegge:
ИСПОЛНЕНИЕ КОРОЛЕВСКИХ ПОСТАНОВЛЕНИЙ[*] В ЦАРСТВЕ СУЩЕСТВИТЕЛЬНЫХ

[*] - игра слов. В оригинале заголовок одновременно означает "Казнь в Царстве Существительных" (execution).

Hello, world!
Здравствуй, мир! Сегодня мы послушаем рассказ о Злом Царе Явы и его борьбу за всемирное уничтожение глаголов.

ПРЕДУПРЕЖДЕНИЕ: у этой сказки нет счастливого конца. Эта сказка не для слабых духом ни для критикующего гласа. Если вы легко обижаетесь или превращаетесь в противного парня в своих комментариях, немедленно перестаньте читать.

Прежде чем начать, разберемся с одним концептуальным изгибом.


The Garbage Overfloweth
МУСОР ПЕРЕПОЛНЯЕТ МУСОРНОЕ ВЕДРО


Все явисты любят диаграммы "use cases", с них и начнем: т.е. вынесем мусор. В том смысле, как во фразе "Джонни, вынеси мусор! Он уже через верх валится!"

Если вы - обычный, нормальный англоговорящий человек, и вас попросили описать вынос мусора, вы наверно думаете о нем примерно так:
    - достать мусорный (пластиковый) мешок из-под раковины
    - вынести его в гараж
    - запихнуть его в большое (пластиковое) ведро [которые в американских городах выставляют к бордюру и их собирают раз в день мусорщики - переводкчик]
    - вернуться в дом
    - вымыть руки
    - шлепнуться на диван
    - продолжать играть в видеигру (или что вы там делали)

Даже если вы не говорите по-английски ..... это - серия действий.
Наши мысли полны отважными, стремительными, эмоциональными действиями. Мы живем, боимся, едим, пьем, идем, останавливаемся, или выносим мусор.
.......

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

Изменения требуют действия, т.е. глагола.

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

Сейчас я расскажу вам сказку о царстве, где произошло именно это.


The Kingdom of Nouns
КОРОЛЕВСТВО СУЩЕСТВИТЕЛЬНЫХ


В Явском королевстве, где Король Ява правит держа всех в кремниевом кулаке, людям не позволено думать как нам с вами. В Явалэнде, панимаишь, существительные очень важны, по приказу самого короля. Существительные - главные подданные королевства. Они расхаживают выглядя аристократично в ярких одеждах, которую поставляют Прилагательные, вполне довольные своей судьбой. Прилагательные принадлежат классу пониже, но они не могут нарадоваться, что не родились Глаголами.

Потому что судьба граждан-Глаголов в королестве очень, очень дурна.

В Явалэнде, по повелению Короля Явы, всеми Глаголами владеют Существительные. Они, по сути, рабы королевства, или по крайней мере слуги или временные слуги (лишенные прав до состояния рабства). Жителей Явалэнда вполне устраивает такая ситуация, и они вряд ли подозревают что все может быть устроено иначе.

Глаголы в Явалэнде делают всю работу, но поскольку их презирают все сословия, ни одному Глаголу не позволено передвигаться самостоятельно. Если Глагол появляется в публичном месте, то всегда только в сопровождении Существительного.

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

Король, посоветовавшись с Богом Солнца по такому важному вопросу, иногда угрожал полностью изгнать всех Глаголов из Королевства Явы. Если такое случится, то жителям понадобится хотя бы один Глагол чтобы делать за них всю работу, и Король, обладая довольно жестоким чувством юмора, решил, что им останется глагол "ИсполнитьОкончательныйУказ" [по-английски - игра слов: execute означает одновременно "исполнить" на официальном языке, и "казнить" -- переводчик]

Глагол "Исполнить" (и его кузины-синонимы "делать", "начать", "пшел", "давайделай", и "впередвперед") могут сделать работу любого другого Глагола подменив его соответствующим ИсполнительнымСуществительным, и вызовом "исполнить()".
Вам нужно почистить зубы? - ЗубоЧиститель(моиЗубы).вперед()
Вынести мусор? -МусорВыносительПланоИсполнитель.давайделай()

В наиболее патриотических провинциях Явалэнда Существительные полностью выдавили Глалогы. Случайному наблюдателю может показаться, что глаголы все еще мелькают, здесь и там, перепахивая поля и вынося господские ночные горшки. Но если присмотреться, вскорости открывается секрет: Существительные могут просто менять имена своих Глаголов-исполнителей(), называя слуг по имени господ (что не изменяет их работы ни малейшим образом). Когда вы заметите,что ПолеВспашка "пашет()", НочноГоршкоОпорожнитель "выливает()", или МэнеджерРегистрации неожиданно сам "регистрирует()", на самом деле вы встретились с армией "Исполнителей.Указов" злого короля, переодетых в одежды своих Существительных



Verbs in Neighboring Kingdoms
О ГЛАГОЛАХ В СОСЕДНИХ КОРОЛЕВСТВАХ


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

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


В этих королевствах нет нужды создавать существительные-обертки для глаголов. У них не появляется ни МусороВыносительнойСтратегии, ни МусороВыносителеЦелеУказания для обнаружения куда идти с мешком в гараже, ни ПослеМусороВынесеннойАкцииОбратногоВызова
прежде чем вы сможете вернуться на свой диван. Они прости пишут глаголы, работающие с существительными, а за тем запускают главный глагол, "вынести_мусор()", включающий мелкие под-действия в нужном порядке.

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

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

Явалэндеское население посматривает на соседей с презрением. Так и живут люди в Программистских Королевствах.


If You Dig a Hole Deep Enough...
ЕСЛИ КОПАТЬ ОЧЕНЬ ГЛУБОКО...

[игра слов: первая часть современной американской поговорки: если выкопать очень глубокую яму, попадешь в Китай; также см. "deep hole to China" - перев.]

На другой стороне мира есть малонаселенные земли, в чьих королевствах Глаголы оказываются среди знати. Это - Функциональные Королевства, которые включают Хаскелию, Окамлику, Схемерию и несколько других. Их жители редко встречают граждан земель в округе Явалэнда. Поскольку рядом с ними мало кто живет, и когда им нечем заняться, они ведут войны друг с другом.

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

В результате, глаголы оказываются исполнительной властью, извините за выражение. Путешественнику легко может показаться, что Глаголы (т.е. функции) - самые важные подданные. Именно поэтому королевства называют Функциональными, а не Вещественными.

На самом отшибе за Функциональными Королевствами, лежит былинная земля под названием Лямбда Окраинная (Lambda the Ultimate). В этом месте, рассказывали люди, вообще не водятся "существительные"! Там живут только глаголы! Конечно, в королевстве есть "вещи", но все они созданы из глаголов, даже сами числа чтобы пересчитывать овец [игра слов: lambda - lamb, звучат похоже -- переводчик], которые оказывается самая популярная форма денег для обменов в тех краях, если слухи не врут. Например, 0 - просто лямбда(), а 1 - лямбда(лямбда()), 2 - (лямбда(лямбда(лямбда()))) и т.д. Каждое, глагол ли, существительное или что-то еще, сконструировано из Главного Глагола, "Лямбда".

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




Are Javalanders Happy?
СЧАСТЛИВЫ ЛИ ЯВИСТЫ?


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

      [Напомню стишок-поучение - перев]:
      Не было гвоздя — подкова пропала
      Не было подковы — лошадь захромала
      Лошадь захромала — командир убит
      Конница разбита, армия бежит.
      Враг вступает в город, пленных не щадя
      Оттого, что в кузнице не было гвоздя.


    Не было гвоздя - "брось новое ПодковоГвоздьНеНайденИсключение("гвоздей нет!")

    Не было подковы - "ЛошадеДоктор.получитьМестнуюКопию().получитьЛошадеДиспетчер().давайделай()

    Лошадь захромала - СкаковойКлуб.получитьСсписокОповещенияПодписчиковНаездников().получитьРассылателя().вперед(новое СообщениеРассылка(Конюшня.получитьНулевойЛошадеОбразец()))

    ............/и так далее/.............


    For the lack of a nail,
        throw new HorseshoeNailNotFoundException("no nails!");
    
    For the lack of a horseshoe,
        EquestrianDoctor.getLocalInstance().getHorseDispatcher().shoot();
    
    For the lack of a horse,
        RidersGuild.getRiderNotificationSubscriberList().getBroadcaster().run(
          new BroadcastMessage(StableFactory.getNullHorseInstance()));
    
    For the lack of a rider,
        MessageDeliverySubsystem.getLogger().logDeliveryFailure(
          MessageFactory.getAbstractMessageInstance(
            new MessageMedium(MessageType.VERBAL),
            new MessageTransport(MessageTransportType.MOUNTED_RIDER),
            new MessageSessionDestination(BattleManager.getRoutingInfo(
                                            BattleLocation.NEAREST))),
          MessageFailureReasonCode.UNKNOWN_RIDER_FAILURE);
    
    For the lack of a message,
        ((BattleNotificationSender)
          BattleResourceMediator.getMediatorInstance().getResource(
            BattleParticipant.PROXY_PARTICIPANT,
            BattleResource.BATTLE_NOTIFICATION_SENDER)).sendNotification(
              ((BattleNotificationBuilder)
                (BattleResourceMediator.getMediatorInstance().getResource(
                BattleOrganizer.getBattleParticipant(Battle.Participant.GOOD_GUYS),
                BattleResource.BATTLE_NOTIFICATION_BUILDER))).buildNotification(
                  BattleOrganizer.getBattleState(BattleResult.BATTLE_LOST),
                  BattleManager.getChainOfCommand().getCommandChainNotifier()));
    
    For the lack of a battle,
        try {
            synchronized(BattleInformationRouterLock.getLockInstance()) {
              BattleInformationRouterLock.getLockInstance().wait();
            }
        } catch (InterruptedException ix) {
          if (BattleSessionManager.getBattleStatus(
               BattleResource.getLocalizedBattleResource(Locale.getDefault()),
               BattleContext.createContext(
                 Kingdom.getMasterBattleCoordinatorInstance(
                   new TweedleBeetlePuddlePaddleBattle()).populate(
                     RegionManager.getArmpitProvince(Armpit.LEFTMOST)))) ==
              BattleStatus.LOST) {
            if (LOGGER.isLoggable(Level.TOTALLY_SCREWED)) {
              LOGGER.logScrewage(BattleLogger.createBattleLogMessage(
                BattleStatusFormatter.format(BattleStatus.LOST_WAR,
                                             Locale.getDefault())));
            }
          }
        }
    
    For the lack of a war,
        new ServiceExecutionJoinPoint(
          DistributedQueryAnalyzer.forwardQueryResult(
            NotificationSchemaManager.getAbstractSchemaMapper(
              new PublishSubscribeNotificationSchema()).getSchemaProxy().
                executePublishSubscribeQueryPlan(
                  NotificationSchema.ALERT,
                  new NotificationSchemaPriority(SchemaPriority.MAX_PRIORITY),
                  new PublisherMessage(MessageFactory.getAbstractMessage(
                    MessageType.WRITTEN,
                    new MessageTransport(MessageTransportType.WOUNDED_SURVIVOR),
                    new MessageSessionDestination(
                      DestinationManager.getNullDestinationForQueryPlan()))),
                  DistributedWarMachine.getPartyRoleManager().getRegisteredParties(
                    PartyRoleManager.PARTY_KING ||
                    PartyRoleManager.PARTY_GENERAL ||
                    PartyRoleManager.PARTY_AMBASSADOR)).getQueryResult(),
            PriorityMessageDispatcher.getPriorityDispatchInstance())).
          waitForService();
    


    All for the lack of a horseshoe nail.


И все - из-за одного гвоздя

Глубокая мудрость, верная до сегодняшнего дня




[anonym_mouse, переводчик]
К этому добавлю от себя. К моему удивлению, первые комментаторы перевода в ru_programming просто не поняли о чем речь в этом отрывке (!!!)

А речь идет о логике программирования вообще. Человеческий язык в огромной степени "встроен" в наш мозг, и его структуры отражают структуры мышления самым точным и тонким образом. Ученым нужно делать не ЯМР-сканы мозга, а копаться в устройстве человеческой речи.

Поэтому ИДЕАЛЬНЫЙ язык программирования должен позволять ЕСТЕСТВЕННЫЙ для человека лингвистический разбор задачи на куски и их изложение.

Изложенные в заметке парадигмы - императивная (пойди, возьми, положи, повернись, вернись) и "объектно-ориентированная" - не охватывают всего. Упомянута функциональная. На псевдо-Лиспе она выглядит примерно так:
(цель - вынести мусор):
    (или 
             (и 
               (засунуть (переместить (взять мешок) в_гараж) (поднять крышку_бака)) 
               (вернуться) 
               (лечь на диван)
             ) 
             (сообщить о провале операции)
         )
    

"взять мешок" дает "существительное", состояние (вроде "мешок в руках"), которое может стать дополнением глагольной формы "переместить что куда", далее оно вложено в "засунуть что куда", где последнее "куда" также получилась из вложенного "поднять что", и так далее

Когда существительное (состояние и т.д.) образуется в результате действий над предыдущим состоянием или предметом (которых, _исходных_ предметов и их собраний, в программе относительно мало) - и вместо того, чтобы давать ему имя, мы анонимно вкладываем его в следующее по цепочке действие

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

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

    (цель) ДОМ :- (зависит от) крыша, стены, окна, двери
    
    Крыша:- стропила, черепица
    
    стропила:- дерево_подготовить, конструкцию_поднять, конструкцию_собрать
    
    конструкцию_собрать:
        (конкретные вычисления-операторы через и, или, ...)
    

Это - логическое программирование, Пролог.
Заметьте, что я не занимаюсь выстраиванием линейной последовательности исполнения, как в случае декларативного программирования с функциями. Я думаю локально, о соседнем уровне, оставляя глобальные увязки программе

Мне возразят - но главное в логическом программировании - декларативность (сначала "описание", потом "вопросы" к нему). Нет, это лишь одна сторона языка, для работы с "базами данных", считайте его языком со встроенной БД. Самое частое и поразительно эффективное применение пролога - просто в логическом разбиении на подзадачи. Он - самая естественная парадигма, с помощью которой мозгу легче всего справиться с огромным материалом, просто потому что это отделяет конкретные вычисления от мета-уровня и позволяет "думать локально", но получать при этом верные глобально результаты.
(Тут можно возразить, что в этом качестве мой пример полностью эквивалентен разбиению на подпрограммы и функции - но странный трюк, который голова проделывает с нами, в том, что функции менее очевидны, чем мышление в терминах "целей". Оно поразительно прибавляет сил - и масштаб куска кода, с которым ты можешь справиться чисто "в уме").

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


Я для себя делаю из всего этого один, для меня очевидный вывод:
поскольку все выдуманные до сего дня парадигмы языков программирования в какой-то мере "естественны" (возможно, для одного круга задач, тогда как другие - для других), то ИДЕАЛЬНЫЙ ЯЗЫК ПРОГРАММИРОВАНИЯ должен позволять выражать на себе НЕ громоздко, но кратко и изящно, ВСЕ парадигмы, т.е. подходы и логику разбиения для увязки верхнего уровня программирования - при том снабжая программиста лёгкими операторами для конкретных задач (строк, хэшей, стеков, связи с операционкой и т.д.).

Идеальный язык обязан быть "мультипарадигмальным" без вымученности.
linkReply

Comments:
From: gds
2008-02-19 07:00 am (UTC)
если попытаться сделать ленивые значения в user-space, то будет что-то вроде такого:
type mylazy 'a = ref (mylazy_cont 'a)
 and mylazy_cont 'a =
  [ Deferred of (unit -> 'a)
  | Value of 'a
  ]
;

value (of_func : (unit -> 'a) -> mylazy 'a)
  f =
    ref (Deferred f)
;

value (of_val : 'a -> mylazy 'a)
  v =
    ref (Value v)
;

value (force : mylazy 'a -> 'a)
  l =
    match l.val with
    [ Deferred func ->
        let v = func () in do
          { l.val := Value v
          ; v
          }
    | Value v ->
        v
    ]
;

Реальные ленивые значения представлены более эффективным образом, с поддержкой на уровне компилятора и рантайма ("some purple magic is going on here"), с правильной обработкой исключений, кидаемых внутри lazy (..) и с обнаружением неправильных рекурсивных значений вида let rec x = lazy (Lazy.force x).

Затраты на создание значения lazy (expr) равны затратам на создание closure вида fun () -> expr. Даже не знаю, как объяснить, много это или мало. Примерно как выделение блока на несколько слов (смотря сколько значений попадают в closure, то есть, сколько значений будет использовано для вычисления expr при вычислении значения). Учитывая, что у окамла выделение памяти весьма быстрое, то оверхед не настолько велик.
Если ленивое значение будет по-любому вычислено, то лучше его вычислить сразу.
Если немалая часть значений будет вычислена по-любому, то немного эффективнее делать не lazy (expr), а Lazy.lazy_from_val (expr). Результатом будет тоже ленивое значение, только вычисленное сразу.
Обилие ленивых значений сильно мешает профилированию. Размазывает время выполнения по всей программе. Поэтому советую использовать только там, где это реально необходимо.
(Reply) (Parent) (Thread)