juan_gandhi: (Default)
Juan-Carlos Gandhi ([personal profile] juan_gandhi) wrote2008-05-14 12:56 pm

dynamic vs static

Я не собираюсь вступать в эту нонешнюю дискуссию. Просто хочу заметить, что речь идёт о "гарвардской архитектуре" супротив "архитектуры фон Неймана".

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

Во второй что данные, что код, без разницы; код - это вид данных, а данные могут на определённом уровне интерпретироваться как код, или строить код, который эти данные сынтепретирует (как, помню, была какая-то база, которая строила классы для доступа прямо при открытии таблицы: взял таблицу - вот и классы загрузились; естественно, что динамически.

Ну а так, наверное, стоит почитать, конечно: Егге устраивает разгром в столице статической типизации

[identity profile] itman.livejournal.com 2008-05-14 08:28 pm (UTC)(link)
Чего-то я не понял как Джава "make uses of multicores" while "c++ doesn't". Что это за интересное такое заявление?

[identity profile] ivan-gandhi.livejournal.com 2008-05-14 09:48 pm (UTC)(link)
А, это вот о чём. JVM справляется с этим без участия человека; thread запустил, а он уж куда-нибудь попадёт. И доступ к переменным организован, с самого начала, правильным образом.

[identity profile] itman.livejournal.com 2008-05-14 09:56 pm (UTC)(link)
Ну а плюсовый тред не туда попадет?
PS: а что такое правильный доступ к переменным?

[identity profile] ivan-gandhi.livejournal.com 2008-05-14 10:26 pm (UTC)(link)
Правильный доступ - это обеспечение синхронизации.

[identity profile] itman.livejournal.com 2008-05-14 11:51 pm (UTC)(link)
Я не очень понимаю, как все это сказывается на производительности. Средства синхронизации и треды в плюсах имеются, я верю, что JVM может выжимать какие-то проценты доппроизводительности из многоядерного процессора, но я не верю
а) Просто интерпретируемая Джава (без JIT) быстрее плюсов
б) Даже Джаве с JIT на самом деле довольно непросто догнать плюсы, потому как расходы на компиляцию на месте нужно компенсировать за счет множества обращений
в) Автоматическую сборку мусора пока еще не отменял
г) Ну, и наконец в Джаве нет (или уже есть)? inline кода и C++-like шаблонов.
По модулю всех этих утверждений, с трудом верится, что нонче Джава быстрее плюсов.

[identity profile] ex-zadoff59.livejournal.com 2008-05-14 11:01 pm (UTC)(link)
кури мутексы чтоли

[identity profile] ivan-gandhi.livejournal.com 2008-05-14 11:10 pm (UTC)(link)
Щас разбежался. Слишком мелкая вещь для джавного программиста.

[identity profile] relyef.livejournal.com 2008-05-15 12:44 am (UTC)(link)
Сейчас Вы скажете, что Java поддерживает threads affinity :)

[identity profile] ivan-gandhi.livejournal.com 2008-05-15 06:22 pm (UTC)(link)
Это язык врага, "thread affinity". Всё что у джавы в этом смысле есть - это threadlocal. Можем, если захочем.

[identity profile] relyef.livejournal.com 2008-05-15 06:25 pm (UTC)(link)
Вопрос на засыпку - io-completion API - это тоже "язык врага"?

[identity profile] relyef.livejournal.com 2008-05-15 10:12 pm (UTC)(link)
Что - ну? Да или нет?

[identity profile] mikkim08.livejournal.com 2008-05-16 10:32 am (UTC)(link)
Это асинхронный i/o. А "io-completion API" там где ?

[identity profile] anton-solovyev.livejournal.com 2008-05-14 08:53 pm (UTC)(link)
Долго думал чем мне не нравится Python (vs Java).

Решил, что я -- neat freak и код на Python получается сравнительно messy, а Java enforces some discipline. Perl -- еще хуже.

А, вот еще как можно сравнивать языки: качество стандартных библиотек. С Питоном все время нарываешься на то, что нажал чуть посильнее и оно ползет по швам. Java терпит куда больше abuse. Все время лезет в голову сравнение велосипеда с грузовиком.

[identity profile] ivan-gandhi.livejournal.com 2008-05-14 09:50 pm (UTC)(link)
Да это верно, конечно. У питона есть ещё такое странное свойство, что на нём надо уметь хорошо писать, чтобы получался читабельный код; а на джаве, как на бейсике, каждый второй идиот может накропать что-то работающее. Правда, этот идиот потом тебя же будет учить правильному стилю; вот тут-то и засада.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-14 11:46 pm (UTC)(link)
Messy? Ужос, ужос. После аккуратного, краткого питоновского кода на многократно более длинный и излишне детальный явский код мне смотреть напряжно.
(А приходится: сопрягаю то и другое.)

[identity profile] ivan-gandhi.livejournal.com 2008-05-14 11:55 pm (UTC)(link)
Кто б возражал. Я просто видел этот ужас. И сам, хм, одно время занимался переводами с питона на джаву. И видел как пишет Алекс Мартелли. Это стихи.

[identity profile] mikkim08.livejournal.com 2008-05-15 05:32 am (UTC)(link)
И видел как пишет Алекс Мартелли. Это стихи.

А например ? (люблю красивый код смотреть)

[identity profile] ivan-gandhi.livejournal.com 2008-05-15 06:26 pm (UTC)(link)

def allstrings(alphabet):
    yield ''
    for astring in allstrings(alphabet):
        for letter in alphabet:
            yield astring + letter 
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 05:14 pm (UTC)(link)
чума! 8-)

[identity profile] glader.livejournal.com 2008-05-19 05:59 am (UTC)(link)
А где еще его красивости можно посмотреть?

[identity profile] ivan-gandhi.livejournal.com 2008-05-19 06:03 pm (UTC)(link)
В гугловской codebase?

[identity profile] glader.livejournal.com 2008-05-19 06:15 pm (UTC)(link)
Если ты про http://code.google.com/search/#q=Martelli , то там я ничего внятного не нашел. Будь добр, объясни чуть поподробнее, если не трудно.

[identity profile] ivan-gandhi.livejournal.com 2008-05-19 07:30 pm (UTC)(link)
:) Я имел в виду внутреннюю codebase...

[identity profile] anton-solovyev.livejournal.com 2008-05-15 12:01 am (UTC)(link)
Я думаю, это оттого, что к питоновскому коду требования ниже. Там тебе и глобальный лок внутри самого Питона, и все коллекции синхронизированы и всерьез эти программы (и библиотеки) никто за пределами коротких скриптов не проверял...
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-15 12:27 am (UTC)(link)
Всерьёз не проверял? Настолько не проверял, что почему-то Google AppEngine из всех языков поддерживает именно питон? :)

Уверяю вас, питон используется гораздо шире и интенсивнее, чем может показаться.

[identity profile] anton-solovyev.livejournal.com 2008-05-15 12:43 am (UTC)(link)
В широте использования сомнений нет, на Питоне тут стоим.

Однако, например, при попытке всерьез использовать что-то типа xmlrpclib или SimpleXMLRPCServer оно начинает позорно трещать по швам.

Деталей не помню, но осадочек остался.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 05:16 pm (UTC)(link)
Да, не все библиотеки одинаково толковы :( Притом xmlrpc как-то особенно крива, но даже и urllib-ы всякие, без которых вроде бы никуда, местами не без косяков.

Это я почему-то счёл, что "всерьёз неикто не проверял" у вас про коллекции и прочие базовые вещи.

[identity profile] trurle.livejournal.com 2008-05-15 05:16 am (UTC)(link)
Аккуратность кода не измеряется в байтах.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 05:20 pm (UTC)(link)
Беда в другом: число строк, которые человек способен удержать в сознании, невелико, и потому число строк, которые можно написать / отладить за данное время, не сильно отличается от языка к языку (хотя, конечно, разные языки отличаются уровнем многословия: на java можно за час успеть написать 20 осмысленных строк, а на J — одну-две).

Потому чем компактнее язык упаковывает понятия, тем он больше (потенциально) увеличивает производительность разработки. Разумеется, при достаточно хорошем им владении (а про learning curve мы тут вроде и не начинали пока).

А вот и не жаль

[identity profile] trurle.livejournal.com 2008-05-18 05:29 pm (UTC)(link)
число строк, которые человек способен удержать в сознании, невелико, и потому число строк, которые можно написать / отладить за данное время, не сильно отличается от языка к языку (хотя, конечно, разные языки отличаются уровнем многословия: на java можно за час успеть написать 20 осмысленных строк, а на J — одну-две).
Это утверждение неверно. Ограничено число концепций и связей, которые человек может удержать в голове, многословность же или сжатость изложения этих концепций имеет совершенно второстепенное значение. Более того, подробное и однозначное изложение может быть преимуществом.
Потому чем компактнее язык упаковывает понятия, тем он больше (потенциально) увеличивает производительность разработки
Вам должен был понравиться APL в его силе и славе, со спецсимволами для операторов. Жаль только что не все программисты разделяют Ваши вкусы.
nine_k: A stream of colors expanding from brain (Default)

Re: А вот и не жаль

[personal profile] nine_k 2008-05-18 06:57 pm (UTC)(link)
О прямом наследнике APL я упомянул выше, под именем J :)

Связи возникают между всеми объектами, которые участвуют в исходном коде. Не все из них вызывают одинаковое умственное напряжение, но все вызывают ненулевое. Разумеется, над закорючками APL-образных языков приходится думать в среднем больше на едимницу длины кода. Но это, увы, не значит, что конструкции вида List<Pair<int, String>> lst = new ArrayList<Pair<int, String>>() проходят для мозга по той же цене, что lst = [].

Re: А вот и не жаль

[identity profile] trurle.livejournal.com 2008-05-18 07:41 pm (UTC)(link)
Но это, увы, не значит, что конструкции вида List
[Error: Irreparable invalid markup ('<pair<int,>') in entry. Owner must fix manually. Raw contents below.]

<i>Но это, увы, не значит, что конструкции вида List<Pair<int, String>> lst = new ArrayList<Pair<int, String>>() проходят для мозга по той же цене, что lst = [].</i>
Разумеется, не по той же. Понять что именно скрывается за конструкцией lst = [] гораздо сложнее чем List<Pair<int, String>> lst = new ArrayList<Pair<int, String>>().
Потому что lst = [] может обозначать тучу разных вещей, о природе которых надо задумываться глядя на эти криптическую запись.
nine_k: A stream of colors expanding from brain (Default)

Re: А вот и не жаль

[personal profile] nine_k 2008-05-18 08:39 pm (UTC)(link)
Задумываться надо и там, и там. Но о разном. Иногда выгоднее одно, иногда другое.
Мне мои задачи расчёта движения денег было проще писать на питоне, и на ошибки типизации я как-то вроде не нарывался. Самое распространённое было — NPE в личине "Object 'None' has no methos %s" или NameError-ы в плохо оттестированном коде.

[identity profile] faceted-jacinth.livejournal.com 2008-05-14 09:06 pm (UTC)(link)
Егге, по-моему, не видит леса за деревьями. В смысле, несмотря на какбэ критическую установку, всё равно воспринимает язык как данное, а стандартные метафоры -- как непреложные правила (в смысле: я ж могу на чистом С и объекты делать, и динамическую типизацию, никто мне не запретит, потому что эскейпы обязаны быть). И, да, судит о статической типизации по наиболее упёртым представителям, которые пытаются запретить эскейпы.

Я сейчас к одной своей проге простенький скриптовый язык приделываю и у меня всякие полуоформленные мысли в связи с этим появляются. Например: существует заговор: тайное общество поклонников динамически типизированных языков называет "dynamic scope" "lexical scope"-ом, чтобы никто не задумался случайно. Ведь когда пишешь интерпретируемый язык, он натурально получается динамически типизированным и с динамическим scope, это baseline, а чтобы получить lexical (static!) scope нужно приложить неслабые усилия, причём централизованно. Но необходимо, и в том же питоне, кстати говоря, все вызовы обычных функций можно отрезолвить в адреса на этапе компиляции (да они наверное и резолвятся), удивительно если подумать, не правда ли?

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

[identity profile] ivan-gandhi.livejournal.com 2008-05-14 09:51 pm (UTC)(link)
Ну ведь Стиви, как он ни пыжся, а всё равно остаётся сельским механизатором. Желающим всех научить законам физики.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-14 11:52 pm (UTC)(link)
> в том же питоне, кстати говоря, все вызовы обычных функций можно отрезолвить в адреса на этапе компиляции

Вынужден вас разочаровать. Нету там никакого этапа компиляции. Все операторы (в т.ч. class и def) исполняются в runtime. Можно на ходу добавить к классу или конкретному объекту какие угодно новые методы, а старые нагло удалить. Поэтому резолвится всё в момент вызова.

Такая же точно картина в javascript и smalltalk.

Потому с этими штуками так удобно играться интерактивно и так трудно писать к ним осмысленны
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-14 11:52 pm (UTC)(link)
...осмысленный JIT.

[identity profile] faceted-jacinth.livejournal.com 2008-05-15 08:35 am (UTC)(link)
i = 1
def a():
    print i
    i = 2
a()

Where is your God now?
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 05:10 pm (UTC)(link)
Хотите ли вы сказать, что исполнение оператора def в этом примере привязывает значение i к тому объекту, который был в момент исполнения? Думаю, вряд ли вы так полагаете :) Каждый раз при исполнении функции a() происходит name lookup для i в окружающем scope (как я понимаю, инструкция LOAD_NAME делает именно это).

В наезде выше, разумеется, под "компиляцией" имеются в виду преобразования c разрешением ссылок (early binding; в этом смысле финальный этап компиляции доделывает линкер), как это происходит в, условно говоря, наследниках алгола. Тут я выразился неверно. Разумеется, компиляция собственно исполнимого кода в более простую форму (типа байткода) возможна и практикуется и в динамических языках.

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 05:27 pm (UTC)(link)
Я хочу сказать, что
>>> def a():
...  print i
...  i = 2
...
>>> a()
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 2, in a
UnboundLocalError: local variable 'i' referenced before assignment

Если закомментить присваивание, то ничего подобного не происходит, естественно.

Это означает, что при определении функции питон аккуратно _компилирует_ её тело, строя табличку локальных переменных, направляя обращения в эту табличку и всё такое. Если бы питон был чисто интерпретируемым, у него, конечно, не было бы возможности кинуть эксепшен на строчке "print i" ДО ТОГО, как выполнилась строчка "i = 2", определяющая i как локальную переменную.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 06:39 pm (UTC)(link)
Это очень здравый довод. Но это не совсем то :)

Это было введено в питоне 2.0.
"An attempt has been made to alleviate one of Python's warts, the often-confusing NameError exception when code refers to a local variable before the variable has been assigned a value. For example, the following code raises an exception on the print statement in both 1.5.2 and 2.0; in 1.5.2 a NameError exception is raised, while 2.0 raises a new UnboundLocalError exception."

Несомненно, питоновский код компилируется в некий (высокоуровневый) байт-код, потому что это эффективнее, чем ходить всё время по дереву разбора. При этом, однако, имена *не* разрешаются окончательно. Хотя есть оптимизация, позволяющая более дёшево получать доступ к именам, про которые мы знаем, что они локальные (потому что раньше по ходу компиляции делали их инициализацию) — инструкция LOAD_FAST.

Вовсе *не компилятор* ругается на эту функцию (как он поругался бы на синтаксическую ошибку). Exception возникает при *исполнении* готового объекта (что, надо сказать, иногда бывает неприятным сюрпризом). Это LOAD_FAST, вставленный компилятором, ругается, afaict. Но так как он умный и понимает, что не найти он мог только локальную переменную, ругается он при помощи более конкретного UnboundLocalError, а не просто NameError.

Тот факт, что компилятор способен прийти к выводу о локальности переменной, говорит о том, что некоторый небольшой (imho, вполне тривиальный) анализ корректного кода он таки выполняет. Мой point в том, что компиляция эта тривиальна — примерно такую компиляцию исполнял и интерпретатор бейсика на 8-битных машинах моего детства, который тоже ключевые слова в байт-коды превращал и скобочные выражения раскладывал во что-то более быстро вычислимое.

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 07:01 pm (UTC)(link)
Это было в питоне с самого начала, в 2.0 они поменяли тип эксепшена.

Несомненно, питоновский код компилируется в некий (высокоуровневый) байт-код
---
U miss da point, bro. Байткод совершенно не высокоуровенен, да его может и вовсе не быть. Дело в спеках, которые ясно говорят, что локальной считается любая переменная, в которую было (точнее, может быть будет) выполнено присваивание, неважно где. Это, ещё раз повторю, на всякий случай, означает, что питоновский интерпретатор, когда ему скармливают очередную функцию, совершает действия, которые я называю компиляцией. Обработку кода без его исполнения. Довольно сложную обработку.

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

Напоминаю, весь пойнт моего первого коммента заключался в том, что компайл-тайм обработка (в частности, статическая типизация) это бенефит, это подъём над бейзлайном, она доставляет.

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

(в данном конкретном случае у него есть возможность, но она есть не всегда. Однако лично я могу сказать, что аналогичная шарповая проверка не раз спасала меня и я даже привык писать код так, чтобы она сработала, если я вдруг что-то забуду. Если у меня есть out переменная и развесистое дерево вычислений, то я никогда не присваиваю ей дефолтное значение в начале, нет, если вычисления удались, присваиваю результат и выхожу, если нет, присваиваю дефолтное значение и выхожу, это правильно, так надо.)
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 07:59 pm (UTC)(link)
Это да: в 2.0 они стали говорить именно про *локальность*, а не просто ошибку разрешения имён.

Байткод питона куда более "высокоуровнев", чем, скажем, у JVM млм CLR.

"If a name is bound in a block, it is a local variable of that block" — да-да. Но энфорсится это не совсем в момент компиляции, как видим. А проверяется компилятором тривиально. И именно тривиальность компилятора и мешает ему отсечь ошибку. (Ещё можно вспомнить, что связанные с этим определением проблемы решают в 3.0 введением слова nonlocal.)

Несомненно, статический анализ полезен! Беда в том, что он не бесплатен. Статическая типизация решает многие проблемы — и создаёт некоторые проблемы. Динамическая — то же самое, одно лечим, другим расплачиваемся. Например, писанием бесконечных вариантов одной функции с разными типами параметров и практически идентичным телом. Или type cast-ами в некоторых тонких местах, где можно проглядеть ошибку типизпции и влететь в неё в runtime (чего, казалось бы, "не может быть").

В C#, кстати, вносят много полезных фишек, позволяющих, с одной стороны, легче использовать некоторые динамические подходы, с другой — не писать лишних слов.

[identity profile] cmm.livejournal.com 2008-05-19 12:26 pm (UTC)(link)
Несомненно, статический анализ полезен! Беда в том, что он не бесплатен. Статическая типизация решает многие проблемы — и создаёт некоторые проблемы. Динамическая — то же самое, одно лечим, другим расплачиваемся.

тут произошло некоторое смешение, тыкскыть, агенд.

требование явного объявления переменных перед использованием, к примеру (не типов, а имён!), не превращает язык из "динамического" в "статический", но весьма улучшает как компиляцию, так и диагностику.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-19 02:14 pm (UTC)(link)
Совершенно согласен. Статический анализ не сводится к статическому контролю типов.

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 05:29 pm (UTC)(link)
Ой, там ещё вначале
>>> i = 1

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 06:03 pm (UTC)(link)
Кстати, раз у нас тут начался вечер чудных питонооткрытий, там ещё есть забавные моменты. Например:
a.py
import b
print "a"

b.py
import a
print "b"
В качестве самостоятельного упражнения оставляю исполнение "python b.py" и "import b" из интерпретатора (да, оба способа работают, но печатают разное).
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 06:49 pm (UTC)(link)
Не вижу, что в этом удивительного. Всё вполне предсказуемо и не противоречит идее, что import есть оператор, исполнимый в runtime. Более того, код типа
try:
  import x.y.z as X
except ImportError:
  import a.b.c as X
тоже вполне себе широко используется для обеспечения version compatibility.

Чудное открытие становится сделать легче, если дважды подряд позвать import b. Функция импорта умная и кэширует импортированные модули, а повторно импорт того же самого не исполняет (что кажется естественным). Потому и в вашем примере, кстати, не возникает бесконечной рекурсии импорта.

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 07:08 pm (UTC)(link)
Вы опять наступаете на те же грабли. Если я показываю кусок кода и говорю "Зацените", это означает, что, скорее всего, нужно его скормить интерпретатору. Ну, если мы оба вроде понимаем язык, то есть когда я показываю код, я имею в виду, что его исполнение даст результат, отличный от ожидаемого. От того, который вы получили, исполнив код на своём внутримозговом интерпретаторе питона. Потому что я как раз утверждаю, что ваш внутренний интерпретатор неправильно работает.

Ну вот и тут. Реально получаемый результат весьма забавен и заставляет задуматься о том, что же всё-таки кэшируется и как это всё работает.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 07:43 pm (UTC)(link)
Почтеннейший, не надо думать обо мне так плохо. Разумеется, я скормил интерпретатору предложенный вам кусок, и только затем взялся писать объяснения.

Не вижу, где получается результат, отличный от ожидаемого.
$ python
Python 2.4.5 (#2, Apr 17 2008, 13:00:52) 
[GCC 4.2.3 (Debian 4.2.3-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import b
a
b
>>> _

Мы видим, как сначала исполнился импорт модуля a, идущий первой строчкой в модуле b. Он (a) занялся импортом модуля b. Но так как мы прямо вот сейчас уже заняты импортом модуля b, повторно (рекурсивно) его импортировать не надо. Осталось напечатать "a". На этом импорт модуля a завершился, и продолжился импорт (т.е исполнение) модуля b; исполнилась его вторая строчка, напечатавшая "b".

Не вижу, что тут неинтуитивного.

Второй пример разобрать? :) Или что я упускаю?

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 07:46 pm (UTC)(link)
да, именно второй. Очень интересно послушать, потому что я сам этого не понимаю.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 08:34 pm (UTC)(link)
Всегда пожалуйста.

$ python b.py
b
a
b
$ _
Картина та же самая. Будем писать имя файла и строку с комментарием.
b.py:1 — import a, поехали исполнять a.py и записали себе что импортируем a.
a.py:1 — import b, поехали исполнять b.py, ведь мы ни разу не пробовали его импортировать.
b.py:1 — import a, но у нас записано, что мы уже исполняем импорт a, потому второй раз не надо, едем дальше
b.py:2 — print "b", напечатали на экране первое "b".
Тут кончился b.py, который мы исполняли по просьбе import b из a.py, едем по нему дальше.
a.py:2 — print "a", напечатали "a".
Тут кончился a.py, который мы звали по просьбе import a, продолжился b.py, который мы звали из командной строки.
b.py:2 — print "b", напечатали вторую "b".
Тут кончился самый внешний b.py, сказочке конец, интерпретатор вышел.

Как-то так я себе это представляю. Может быть, я где-то серьёзно ошибаюсь, я ещё пороюсь в этом (тем более, надо по работе), но по крайней мере мне моя версия кажется правдоподобной :)

[identity profile] faceted-jacinth.livejournal.com 2008-05-15 08:38 am (UTC)(link)
Ну, это помимо того очевидного факта, что обычный CPython таки компилит питон в байткод, сохраняя его в файликах с расширением ".pyc", так что моё утверждение что имена _обычных_ функций (не методов, а обычных функций) могут быть отрезолвлены в адреса на этапе компиляции остаётся не только истинным, но и абсолютно корректным.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 04:44 pm (UTC)(link)
Байткод остаётся таким же динамическим, это просто грамотно уплощённое AST ;)

Т.е. разрешение имён и разрешение порядка наследования(aka MRO) как происходило в runtime, так и происходит. И любой объект (например, метод объекта или функцию), на которое ссылается заданное имя, всё так же можно переопределить в любой точке исполнения.

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

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 05:33 pm (UTC)(link)
Я всегда открыт для новых открытий, пусть у меня есть модуль fj, в котором на верхнем уровне лежит
def a():
  print "a"

def b():
 a()

у вас же есть свой модуль, в котором вы можете сделать from fj import *, а потом в какой-то момент вызвать b(). Расскажите пожалуйста, как вы сделаете так, чтобы в результате не напечаталось "а"? Ну, раз всё разрешение имён якобы происходит в рантайме, вы же сможете подсунуть моему b() свою а(), правда?
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-18 09:28 pm (UTC)(link)
С удовольствием подсуну. Но дело, как вы правильно замечаете, хитрее, чем кажется.
$ cat fj.py
def a():
   print "fj's a"

def b():
   a()
   foo()
Добавим для наглядности вызов некоторой неизвестно где определённой функции foo, которую надо искать "снаружи".
$ python
Python 2.4.5 (#2, Apr 17 2008, 13:00:52) 
[GCC 4.2.3 (Debian 4.2.3-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from fj import *
>>> b()
fj's a
Traceback (most recent call last):
  File "", line 1, in ?
  File "fj.py", line 6, in b
    foo()
NameError: global name 'foo' is not defined
Всё предсказуемо: зовётся a из нашего модуля, а foo не находится.
>>> def foo():
...   print "foo from outer scope"
... 
>>> b()
fj's a
Traceback (most recent call last):
  File "", line 1, in ?
  File "fj.py", line 6, in b
    foo()
NameError: global name 'foo' is not defined
Ага, foo опять не находится!
Это потому, что scope-ов много, по одному на code block. В частности, у модуля свой scope, имена в импортированном модуле разрешаются только в нём, а "выше", на уровень импортирующего, не заходят. Импортирующий же код не портит нечаянно имена в импортированном.
Но это не значит, что scope модуля нельзя поменять :)
>>> import fj
>>> fj.foo = foo
>>> b()
fj's a
foo from outer scope
Теперь, когда мы имеем удобное имя для модуля, мы лезем в его scope и вписываем туда имя своей функции. И всё срабатывает.
Сстановится вдруг понятно, почему, если 10 импортированных нами модулей вызывают в своём коде функцию a(), они получают каждый свою. И ясно, как подсунуть другую a().
>>> def not_a():
...   print "ceci n'est une a"
... 
>>> fj.a = not_a
>>> b()
ceci n'est une a
foo from outer scope
>>> _

Вот как-то так, мне кажется.

Мои извинения хозяину журнала, которому всё это приходит на почту %) 御免なさい!

[identity profile] faceted-jacinth.livejournal.com 2008-05-19 08:11 pm (UTC)(link)
Ух ты, да, я был неправ. Почему-то мне казалось, что функции должны быть ридонли.

По поводу импорта -- да, видимо так всё и работает. Я хотел сказать, что происходящее в процессе тоже не вполне укладывается в представление о питоне как о чисто скриптовом, с одной стороны импорт вроде бы просто исполняет модуль и добавляет его имя в локальный scope, с другой -- имеем это вот ровно двухкратное исполнение.
nine_k: A stream of colors expanding from brain (Default)

[personal profile] nine_k 2008-05-19 08:16 pm (UTC)(link)
Кстати сказать, пока я не порылся немного в спецификациях, я тоже думал, что я неправ, и готовился вежливо признать поражение :)

Да, если нужен ещё более "чисто скриптовый" — это, наверное, ruby в его исходной реализации, там напрямую по дереву ходят при исполнении (естетственно, это тормозит). В rubinius вроде сделали фазу компиляции во что-то (но не напрямую в байткод jvm).

[identity profile] cmm.livejournal.com 2008-05-15 08:40 am (UTC)(link)
Такая же точно картина в javascript и smalltalk.

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

[identity profile] cmm.livejournal.com 2008-05-15 07:45 am (UTC)(link)
существует заговор: тайное общество поклонников динамически типизированных языков называет "dynamic scope" "lexical scope"-ом, чтобы никто не задумался случайно.

ну вот давайте я задумаюсь.
подумал, подумал.
нет, всё правильно.

Ведь когда пишешь интерпретируемый язык, он натурально получается динамически типизированным и с динамическим scope, это baseline, а чтобы получить lexical (static!) scope нужно приложить неслабые усилия, причём централизованно.

что такое "интерпретируемый язык"?  каким образом динамический скоп легче лексического?  какие такие страшные "централизованные усилия"?
(зачем придумывать ещё один недоязык?)

one cannot, в натуре, rightly comprehend the kind of confusion etc.

[identity profile] faceted-jacinth.livejournal.com 2008-05-15 09:12 am (UTC)(link)
Интерпретируемый язык это язык, в котором практически нет ничего, напоминающего компиляцию.

Под компиляцией я понимаю обработку кода без его исполнения.

Понятно, что совсем без компиляции обойтись тяжело, то же определение функции присваивает её тело куда-нибудь без исполнения, плюс conditional operators явным образом должны читать но не исполнять код в неисполняемой ветке, в этом как бы их смысл, но вопрос в том, насколько много вещей делается в процессе компиляции (которая, конечно, не обязана быть выделена в отдельный этап).

Так вот, dynamic scope получается совершенно естественно: у нас есть один словарь, в котором хранятся значения символов. Определение функции создаёт там символ [имя функции] и запихивает в него тело функции (про параметры пока не думаем), "i = 1" создаёт (или использует текущий) символ i, в котором оказывается объект инт, равный 1, и так далее.

Но при этом (я буду использовать питоновский синтаксис) такой код:
i = 1
def a():
    print i
a()

def b():
    i = 2
    a()

b()

должен печатать 2. Потому что словарь один на всех. Более того, если всё действительно так просто, то а() может изменить i, которую нам бы хотелось считать локальной переменной b(), определение функции с тем же именем как у определённой в каком-нибудь чужом модуле приводит к удивительным результатам и так далее. Dynamic scope плохой!

А дальше начинаются "страшные централизованные усилия". Можно сделать shadowing, но он всех проблем не решает (тот код по-прежнему будет печатать 2, лямбды работать не будут вообще, потому что не будут лексическими замыканиями) и добавляет свои. Следовательно, нужно делать нормальный static scope: для каждой штуки, обладающей лексическим контекстом (функции, например) либо этот контекст сохранять явно и восстанавливать при вызове, прям весь словарь, либо находить все символы, которые при использовании будут обращаться к контексту и честно компилировать их, то есть заменять на fully qualified обращения, можно сразу по адресам. Как-то так, я этого ещё не делал.

(зачем придумывать ещё один недоязык?) -- вот именно за этим, чтобы увидеть, как некоторые привычные вещи оказываются весьма нетривиальными.

[identity profile] cmm.livejournal.com 2008-05-15 09:32 am (UTC)(link)
Так вот, dynamic scope получается совершенно естественно: у нас есть один словарь, в котором хранятся значения символов. Определение функции создаёт там символ [имя функции] и запихивает в него тело функции (про параметры пока не думаем), "i = 1" создаёт (или использует текущий) символ i, в котором оказывается объект инт, равный 1, и так далее.

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

Интерпретируемый язык это язык, в котором практически нет ничего, напоминающего компиляцию.

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

(зачем придумывать ещё один недоязык?) -- вот именно за этим, чтобы увидеть, как некоторые привычные вещи оказываются весьма нетривиальными.

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

а теория и практика компиляции языков с lexical scope — это, по сравнению с теми пчёлами, сто раз пройденная неинтересная фигня.

Следовательно, нужно делать нормальный static scope: для каждой штуки, обладающей лексическим контекстом (функции, например) либо этот контекст сохранять явно и восстанавливать при вызове, прям весь словарь, либо находить все символы, которые при использовании будут обращаться к контексту и честно компилировать их, то есть заменять на fully qualified обращения, можно сразу по адресам. Как-то так, я этого ещё не делал.

примирить lexical scope с отсутствием хотя бы чего-то типа перловского "my" — это больно, да.  но это чисто питоновская фигня, я к чему.  если бы Гуидо не был (во времена создания Питона, во всяком случае) позорным неучем, то бабушки были бы дедушками etc.

[identity profile] faceted-jacinth.livejournal.com 2008-05-15 09:54 am (UTC)(link)
Я, оказывается, использовал термины почти как они описаны в википедии (http://en.wikipedia.org/wiki/Scope_(programming)#Static_versus_dynamic_scoping), не знаю, насколько это принятая терминология, конечно. Правда, они там включают в него shadowing, ну ладно, спасибо, буду знать.


Про спецификацию: она, конечно, бумажка и всё такое, но в ней есть места, которые могут быть интерпретированы вполне однозначно как требование определённой степени компиляции. Например: я выше в ответ [livejournal.com profile] 9000 забавный кусок питонокода привёл, описывающая подобное поведение спецификация в принципе не может быть реализована иначе чем через предварительный проход по телу функции и построение таблички локальных переменных. Именно это я называю компиляцией, а не неинтересную трансляцию в машинные коды, которая может быть, а может и не быть.


Кстати, Гвидо в момент создания питона уже семь лет был master of science и имел опыт языкотворчества.

[identity profile] cmm.livejournal.com 2008-05-15 10:52 am (UTC)(link)
Кстати, Гвидо в момент создания питона уже семь лет был master of science и имел опыт языкотворчества.

удивительно, правда?

Lisp rules, all other programming languages sucks

[identity profile] trurle.livejournal.com 2008-05-18 05:32 pm (UTC)(link)
См. сабж.
Я тут подумал - как же нам всем остальным повезло что создатели XML не выбрали для него скобочную нотацию. Мысль о том что мы явно живем не в худшем из миров, отчасти примиряет с.

Re: Lisp rules, all other programming languages sucks

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 05:38 pm (UTC)(link)
Кто-то, чуть ли не сам Грэхем, писал когда-то, что иксемель лучше подходит для своей предметной области, чем s-expressions. Не помню точно, где и когда, но было. Аргумент базировался в основном на том, что в случае чего придётся ведь искать глюки, а то и пытаться интерпретировать малформед код, а в этих случаях явное указание того, какой тег закрывается, оказывает неоценимую услугу. А код всё равно программы генерят, им не тяжело.

Re: Lisp rules, all other programming languages sucks

[identity profile] cmm.livejournal.com 2008-05-18 07:10 pm (UTC)(link)
у иксемеля есть предметная область?

Re: Lisp rules, all other programming languages sucks

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 07:11 pm (UTC)(link)
Да, описание иерархии данных.

Re: Lisp rules, all other programming languages sucks

[identity profile] cmm.livejournal.com 2008-05-18 07:21 pm (UTC)(link)
гм.
да, точно, про благостность закрывающих тагов я где-то читал.
правда, Грэм вряд ли писал что-то подобное даже после того как сошёл с ума.

Re: Lisp rules, all other programming languages sucks

[identity profile] faceted-jacinth.livejournal.com 2008-05-18 07:36 pm (UTC)(link)
Он не сошёл с ума, он Узрел Истину! Которая состоит в том, что лисп сосёт в большинстве реальных задач, невзирая на всю его приспособленность к работе с нереальными. И вот он пытается что-то с этим сделать, замена сетф на = уже показывает, что его рассудок ясен как никогда (в отличие от рассудков тех, кому это не нравится), хитрый трюк с make-hash, создающим функцию (реверсибл), а не символ, подтверждает впечатление.

Re: Lisp rules, all other programming languages sucks

[identity profile] cmm.livejournal.com 2008-05-18 07:51 pm (UTC)(link)
я не про Арк, я вообще. :)
совсем человек халтурно писать стал, по-моему.

Re: Lisp rules, all other programming languages sucks

[identity profile] cmm.livejournal.com 2008-05-18 07:59 pm (UTC)(link)
идея починки "сосания" Лиспа путём рихтовки синтаксиса присваивания, впрочем, тоже очень забавна, да.  нефигово, должно быть, помогает в реальных задачах!

Re: Lisp rules, all other programming languages sucks

[identity profile] cmm.livejournal.com 2008-05-18 06:38 pm (UTC)(link)
спрашивается, причём тут Лисп?
(в английском языке не следует добавлять 's' к глаголам третьего лица множественного числа, только единственного.  в прошлый раз я счёл это опечаткой).

Re: Lisp rules, all other programming languages sucks

[identity profile] trurle.livejournal.com 2008-05-18 06:44 pm (UTC)(link)
Мда, в голове образовался copy-paste.
А лисп тут совсем ни при чем, разумеется.

Re: Lisp rules, all other programming languages sucks

[identity profile] cmm.livejournal.com 2008-05-18 07:02 pm (UTC)(link)
А лисп тут совсем ни при чем, разумеется.

если Вам кажется что причём, то я весь уши.

[identity profile] cema.livejournal.com 2008-05-15 12:24 am (UTC)(link)
Скобку закрой. :-)

[identity profile] yosefk.livejournal.com 2008-05-15 05:39 pm (UTC)(link)
Не знаю, насколько это здесь интересно, но гарвардская архитектура, по-моему, лучше, если говорить на машинном уровне (а не в более широком смысле, то есть о воможности какой-либо генерации кода в системе). Машинный код - он малоприятный сорт данных, и генерится не очень часто (даже в хотспотящих виемах, я думаю - бежит же он дольше, чем генерится). Иметь два банка, для кода и для данных, приятней, чем один - bandwidth больше. Налог на генерацию - флашинг кешей - вроде бы того стоит. Конечно, это крайне низкоуровневая мысль.

[identity profile] ex-chrobin.livejournal.com 2008-05-16 07:04 am (UTC)(link)
если я ничего не путаю, гарвардская архитектура предполагает immutable code.

[identity profile] yosefk.livejournal.com 2008-05-16 07:29 am (UTC)(link)
Если он тру гарвард. Но такого никто делать не станет, потому что надо же запускать компиляторы, да и просто процессы менять, и потом это фрагментация RAM. Но есть гарвард кеш, где immutable instruction cache и отдельно data cache. Чтобы поменять код, нужно его написать во внешнюю память, а потом читать в instruction cache. Если смотреть на процессор отдельно от внешней памяти, то это гарвардская архитектура. Так она почти везде - я unified cache один раз только видел.

[identity profile] ex-chrobin.livejournal.com 2008-05-16 07:03 am (UTC)(link)
так если бы дело только в холиворе между принстоном-гарвардом было. ведь мейнстрим диктует ооп, и диктует хмл, в хмл положено отделять код от данных, в ооп — смешивать; можно найти и другие примеры двойной морали. вот и образуется у людей когнитивный диссонанс, и в этом проявляется диалектический материализм..

а егге с каждым разом все более мне жириновского напоминает, только не такой агрессивный.

[identity profile] mikkim08.livejournal.com 2008-05-16 07:27 am (UTC)(link)
Надо ему подсказать как-то что ли.
А то пропадает человек.

[identity profile] mikkim08.livejournal.com 2008-05-16 07:37 am (UTC)(link)
Я работал с одним софтом, который был написан на Джаве, но следующим образом:

Были классы -- чистые value objects. Просто структуры данных с конструкторами и аксесорами. Были классы -- чистые команды. Просто функции.

Но это внутри.

А сверху в качестве API выдавались обычные классы, совмещающие state и behavior.

Я спросил у тех мужиков:
- Вы с "Си" что ли портировали ?
- Нет, - говорят. С нуля так писали. Просто ООП мы тут не любим, но раз народу нравится -- сделали.