Juan-Carlos Gandhi (
juan_gandhi) wrote2008-05-14 12:56 pm
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
dynamic vs static
Я не собираюсь вступать в эту нонешнюю дискуссию. Просто хочу заметить, что речь идёт о "гарвардской архитектуре" супротив "архитектуры фон Неймана".
В первой данные отделены от кода, и задача программиста состоит в том, чтобы состыковать, хоть и через посредников, данные и код, который их обрабатывает; вот и приходится катать всякие конфигурации, эксэмэли, ентити бинзы; всё для того, чтобы уберечь код от данных. Тогда код можно статически проверить и отлить в бронзе. Чтоб не сломался.
Во второй что данные, что код, без разницы; код - это вид данных, а данные могут на определённом уровне интерпретироваться как код, или строить код, который эти данные сынтепретирует (как, помню, была какая-то база, которая строила классы для доступа прямо при открытии таблицы: взял таблицу - вот и классы загрузились; естественно, что динамически.
Ну а так, наверное, стоит почитать, конечно: Егге устраивает разгром в столице статической типизации
В первой данные отделены от кода, и задача программиста состоит в том, чтобы состыковать, хоть и через посредников, данные и код, который их обрабатывает; вот и приходится катать всякие конфигурации, эксэмэли, ентити бинзы; всё для того, чтобы уберечь код от данных. Тогда код можно статически проверить и отлить в бронзе. Чтоб не сломался.
Во второй что данные, что код, без разницы; код - это вид данных, а данные могут на определённом уровне интерпретироваться как код, или строить код, который эти данные сынтепретирует (как, помню, была какая-то база, которая строила классы для доступа прямо при открытии таблицы: взял таблицу - вот и классы загрузились; естественно, что динамически.
Ну а так, наверное, стоит почитать, конечно: Егге устраивает разгром в столице статической типизации
no subject
no subject
no subject
PS: а что такое правильный доступ к переменным?
no subject
no subject
а) Просто интерпретируемая Джава (без JIT) быстрее плюсов
б) Даже Джаве с JIT на самом деле довольно непросто догнать плюсы, потому как расходы на компиляцию на месте нужно компенсировать за счет множества обращений
в) Автоматическую сборку мусора пока еще не отменял
г) Ну, и наконец в Джаве нет (или уже есть)? inline кода и C++-like шаблонов.
По модулю всех этих утверждений, с трудом верится, что нонче Джава быстрее плюсов.
no subject
no subject
no subject
no subject
no subject
no subject
no subject
no subject
no subject
no subject
no subject
Решил, что я -- neat freak и код на Python получается сравнительно messy, а Java enforces some discipline. Perl -- еще хуже.
А, вот еще как можно сравнивать языки: качество стандартных библиотек. С Питоном все время нарываешься на то, что нажал чуть посильнее и оно ползет по швам. Java терпит куда больше abuse. Все время лезет в голову сравнение велосипеда с грузовиком.
no subject
no subject
(А приходится: сопрягаю то и другое.)
no subject
no subject
А например ? (люблю красивый код смотреть)
no subject
no subject
no subject
no subject
no subject
no subject
no subject
no subject
Уверяю вас, питон используется гораздо шире и интенсивнее, чем может показаться.
no subject
Однако, например, при попытке всерьез использовать что-то типа xmlrpclib или SimpleXMLRPCServer оно начинает позорно трещать по швам.
Деталей не помню, но осадочек остался.
no subject
Это я почему-то счёл, что "всерьёз неикто не проверял" у вас про коллекции и прочие базовые вещи.
no subject
no subject
Потому чем компактнее язык упаковывает понятия, тем он больше (потенциально) увеличивает производительность разработки. Разумеется, при достаточно хорошем им владении (а про learning curve мы тут вроде и не начинали пока).
А вот и не жаль
Это утверждение неверно. Ограничено число концепций и связей, которые человек может удержать в голове, многословность же или сжатость изложения этих концепций имеет совершенно второстепенное значение. Более того, подробное и однозначное изложение может быть преимуществом.
Потому чем компактнее язык упаковывает понятия, тем он больше (потенциально) увеличивает производительность разработки
Вам должен был понравиться APL в его силе и славе, со спецсимволами для операторов. Жаль только что не все программисты разделяют Ваши вкусы.
Re: А вот и не жаль
Связи возникают между всеми объектами, которые участвуют в исходном коде. Не все из них вызывают одинаковое умственное напряжение, но все вызывают ненулевое. Разумеется, над закорючками APL-образных языков приходится думать в среднем больше на едимницу длины кода. Но это, увы, не значит, что конструкции вида List<Pair<int, String>> lst = new ArrayList<Pair<int, String>>() проходят для мозга по той же цене, что lst = [].
Re: А вот и не жаль
Разумеется, не по той же. Понять что именно скрывается за конструкцией lst = [] гораздо сложнее чем List<Pair<int, String>> lst = new ArrayList<Pair<int, String>>().
Потому что lst = [] может обозначать тучу разных вещей, о природе которых надо задумываться глядя на эти криптическую запись.
Re: А вот и не жаль
Мне мои задачи расчёта движения денег было проще писать на питоне, и на ошибки типизации я как-то вроде не нарывался. Самое распространённое было — NPE в личине "Object 'None' has no methos %s" или NameError-ы в плохо оттестированном коде.
no subject
Я сейчас к одной своей проге простенький скриптовый язык приделываю и у меня всякие полуоформленные мысли в связи с этим появляются. Например: существует заговор: тайное общество поклонников динамически типизированных языков называет "dynamic scope" "lexical scope"-ом, чтобы никто не задумался случайно. Ведь когда пишешь интерпретируемый язык, он натурально получается динамически типизированным и с динамическим scope, это baseline, а чтобы получить lexical (static!) scope нужно приложить неслабые усилия, причём централизованно. Но необходимо, и в том же питоне, кстати говоря, все вызовы обычных функций можно отрезолвить в адреса на этапе компиляции (да они наверное и резолвятся), удивительно если подумать, не правда ли?
Вот эта параллель меня как-то настораживает. Не обязательно же ведь требовать строгой типизации, достаточно уметь её вычислять, и это умение вроде бы должно быть таким же импрувментом, как и умение делать static scope, ну, если оставлены удобные эскейпы.
no subject
no subject
Вынужден вас разочаровать. Нету там никакого этапа компиляции. Все операторы (в т.ч. class и def) исполняются в runtime. Можно на ходу добавить к классу или конкретному объекту какие угодно новые методы, а старые нагло удалить. Поэтому резолвится всё в момент вызова.
Такая же точно картина в javascript и smalltalk.
Потому с этими штуками так удобно играться интерактивно и так трудно писать к ним осмысленны
no subject
no subject
Where is your God now?
no subject
В наезде выше, разумеется, под "компиляцией" имеются в виду преобразования c разрешением ссылок (early binding; в этом смысле финальный этап компиляции доделывает линкер), как это происходит в, условно говоря, наследниках алгола. Тут я выразился неверно. Разумеется, компиляция собственно исполнимого кода в более простую форму (типа байткода) возможна и практикуется и в динамических языках.
no subject
Если закомментить присваивание, то ничего подобного не происходит, естественно.
Это означает, что при определении функции питон аккуратно _компилирует_ её тело, строя табличку локальных переменных, направляя обращения в эту табличку и всё такое. Если бы питон был чисто интерпретируемым, у него, конечно, не было бы возможности кинуть эксепшен на строчке "print i" ДО ТОГО, как выполнилась строчка "i = 2", определяющая i как локальную переменную.
no subject
Это было введено в питоне 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-битных машинах моего детства, который тоже ключевые слова в байт-коды превращал и скобочные выражения раскладывал во что-то более быстро вычислимое.
no subject
Несомненно, питоновский код компилируется в некий (высокоуровневый) байт-код
---
U miss da point, bro. Байткод совершенно не высокоуровенен, да его может и вовсе не быть. Дело в спеках, которые ясно говорят, что локальной считается любая переменная, в которую было (точнее, может быть будет) выполнено присваивание, неважно где. Это, ещё раз повторю, на всякий случай, означает, что питоновский интерпретатор, когда ему скармливают очередную функцию, совершает действия, которые я называю компиляцией. Обработку кода без его исполнения. Довольно сложную обработку.
И очень плохо, что не настолько сложную, насколько это возможно, так что у Стиви появляется повод восторгаться очередным мастерски произведённым удалением гланд через задний проход.
Напоминаю, весь пойнт моего первого коммента заключался в том, что компайл-тайм обработка (в частности, статическая типизация) это бенефит, это подъём над бейзлайном, она доставляет.
То, что питон кидает эксепшен не во время компиляции, а во время интерпретации, это очень, очень плохо. У него во время компиляции уже есть все данные, он мог бы сообщить об этом тогда, но нет, он зачем-то пытается прикинуться чисто интерпретируемым, неудачно причём.
(в данном конкретном случае у него есть возможность, но она есть не всегда. Однако лично я могу сказать, что аналогичная шарповая проверка не раз спасала меня и я даже привык писать код так, чтобы она сработала, если я вдруг что-то забуду. Если у меня есть out переменная и развесистое дерево вычислений, то я никогда не присваиваю ей дефолтное значение в начале, нет, если вычисления удались, присваиваю результат и выхожу, если нет, присваиваю дефолтное значение и выхожу, это правильно, так надо.)
no subject
Байткод питона куда более "высокоуровнев", чем, скажем, у 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#, кстати, вносят много полезных фишек, позволяющих, с одной стороны, легче использовать некоторые динамические подходы, с другой — не писать лишних слов.
no subject
тут произошло некоторое смешение, тыкскыть, агенд.
требование явного объявления переменных перед использованием, к примеру (не типов, а имён!), не превращает язык из "динамического" в "статический", но весьма улучшает как компиляцию, так и диагностику.
no subject
no subject
>>> i = 1
no subject
В качестве самостоятельного упражнения оставляю исполнение "python b.py" и "import b" из интерпретатора (да, оба способа работают, но печатают разное).
no subject
Чудное открытие становится сделать легче, если дважды подряд позвать import b. Функция импорта умная и кэширует импортированные модули, а повторно импорт того же самого не исполняет (что кажется естественным). Потому и в вашем примере, кстати, не возникает бесконечной рекурсии импорта.
no subject
Ну вот и тут. Реально получаемый результат весьма забавен и заставляет задуматься о том, что же всё-таки кэшируется и как это всё работает.
no subject
Не вижу, где получается результат, отличный от ожидаемого.
Мы видим, как сначала исполнился импорт модуля a, идущий первой строчкой в модуле b. Он (a) занялся импортом модуля b. Но так как мы прямо вот сейчас уже заняты импортом модуля b, повторно (рекурсивно) его импортировать не надо. Осталось напечатать "a". На этом импорт модуля a завершился, и продолжился импорт (т.е исполнение) модуля b; исполнилась его вторая строчка, напечатавшая "b".
Не вижу, что тут неинтуитивного.
Второй пример разобрать? :) Или что я упускаю?
no subject
no subject
Картина та же самая. Будем писать имя файла и строку с комментарием.
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, сказочке конец, интерпретатор вышел.
Как-то так я себе это представляю. Может быть, я где-то серьёзно ошибаюсь, я ещё пороюсь в этом (тем более, надо по работе), но по крайней мере мне моя версия кажется правдоподобной :)
no subject
no subject
Т.е. разрешение имён и разрешение порядка наследования(aka MRO) как происходило в runtime, так и происходит. И любой объект (например, метод объекта или функцию), на которое ссылается заданное имя, всё так же можно переопределить в любой точке исполнения.
Потому гарантировать, что в данной точке программы данное имя разрешается в указанный объект в общем случае становится очень трудно в сравнении со статическими более-менее процедурными языками. (В то время как в чистом fp это, конечно, вообще тривиально.)
no subject
у вас же есть свой модуль, в котором вы можете сделать from fj import *, а потом в какой-то момент вызвать b(). Расскажите пожалуйста, как вы сделаете так, чтобы в результате не напечаталось "а"? Ну, раз всё разрешение имён якобы происходит в рантайме, вы же сможете подсунуть моему b() свою а(), правда?
no subject
Добавим для наглядности вызов некоторой неизвестно где определённой функции foo, которую надо искать "снаружи".
Всё предсказуемо: зовётся a из нашего модуля, а foo не находится.
Ага, foo опять не находится!
Это потому, что scope-ов много, по одному на code block. В частности, у модуля свой scope, имена в импортированном модуле разрешаются только в нём, а "выше", на уровень импортирующего, не заходят. Импортирующий же код не портит нечаянно имена в импортированном.
Но это не значит, что scope модуля нельзя поменять :)
Теперь, когда мы имеем удобное имя для модуля, мы лезем в его scope и вписываем туда имя своей функции. И всё срабатывает.
Сстановится вдруг понятно, почему, если 10 импортированных нами модулей вызывают в своём коде функцию a(), они получают каждый свою. И ясно, как подсунуть другую a().
Вот как-то так, мне кажется.
Мои извинения хозяину журнала, которому всё это приходит на почту %) 御免なさい!
no subject
По поводу импорта -- да, видимо так всё и работает. Я хотел сказать, что происходящее в процессе тоже не вполне укладывается в представление о питоне как о чисто скриптовом, с одной стороны импорт вроде бы просто исполняет модуль и добавляет его имя в локальный scope, с другой -- имеем это вот ровно двухкратное исполнение.
no subject
Да, если нужен ещё более "чисто скриптовый" — это, наверное, ruby в его исходной реализации, там напрямую по дереву ходят при исполнении (естетственно, это тормозит). В rubinius вроде сделали фазу компиляции во что-то (но не напрямую в байткод jvm).
no subject
не везде, насколько я знаю.
из спецификации соответствующих языков ничего подобного точно не следует.
no subject
ну вот давайте я задумаюсь.
подумал, подумал.
нет, всё правильно.
что такое "интерпретируемый язык"? каким образом динамический скоп легче лексического? какие такие страшные "централизованные усилия"?
(зачем придумывать ещё один недоязык?)
one cannot, в натуре, rightly comprehend the kind of confusion etc.
no subject
Под компиляцией я понимаю обработку кода без его исполнения.
Понятно, что совсем без компиляции обойтись тяжело, то же определение функции присваивает её тело куда-нибудь без исполнения, плюс conditional operators явным образом должны читать но не исполнять код в неисполняемой ветке, в этом как бы их смысл, но вопрос в том, насколько много вещей делается в процессе компиляции (которая, конечно, не обязана быть выделена в отдельный этап).
Так вот, dynamic scope получается совершенно естественно: у нас есть один словарь, в котором хранятся значения символов. Определение функции создаёт там символ [имя функции] и запихивает в него тело функции (про параметры пока не думаем), "i = 1" создаёт (или использует текущий) символ i, в котором оказывается объект инт, равный 1, и так далее.
Но при этом (я буду использовать питоновский синтаксис) такой код:
должен печатать 2. Потому что словарь один на всех. Более того, если всё действительно так просто, то а() может изменить i, которую нам бы хотелось считать локальной переменной b(), определение функции с тем же именем как у определённой в каком-нибудь чужом модуле приводит к удивительным результатам и так далее. Dynamic scope плохой!
А дальше начинаются "страшные централизованные усилия". Можно сделать shadowing, но он всех проблем не решает (тот код по-прежнему будет печатать 2, лямбды работать не будут вообще, потому что не будут лексическими замыканиями) и добавляет свои. Следовательно, нужно делать нормальный static scope: для каждой штуки, обладающей лексическим контекстом (функции, например) либо этот контекст сохранять явно и восстанавливать при вызове, прям весь словарь, либо находить все символы, которые при использовании будут обращаться к контексту и честно компилировать их, то есть заменять на fully qualified обращения, можно сразу по адресам. Как-то так, я этого ещё не делал.
(зачем придумывать ещё один недоязык?) -- вот именно за этим, чтобы увидеть, как некоторые привычные вещи оказываются весьма нетривиальными.
no subject
в принятой терминологии это называется global scope. dynamic scope — это когда можно аккуратно подставлять новое значение глобальной переменной на время выполнения делающей это функции. как в емаксе, скажем. отсюда и слово "dynamic", поскольку scope как бы контролируется программой во время выполнения.
язык — это такая бамажка, спецификация (пусть даже чисто абстрактная и выведенная пост-фактум из единственной кривой реализации, как в большинстве современный нам "скриптовых" недоязыков). а компилировать можно практически всё что угодно, хотя некоторые языки и делают некоторые фишки компиляции малопрактичными (например, Питон. то есть там можно спекулятивно резолвить (потенциально-)глобальные имена во время компиляции, скажем, но вряд ли стоит. но можно).
я Вам страшный вещь скажу: если иметь на старте голую железку, так даже распечатать букву "а" на экран покажется весьма нетривиальным. :)
а теория и практика компиляции языков с lexical scope — это, по сравнению с теми пчёлами, сто раз пройденная неинтересная фигня.
примирить lexical scope с отсутствием хотя бы чего-то типа перловского "my" — это больно, да. но это чисто питоновская фигня, я к чему. если бы Гуидо не был (во времена создания Питона, во всяком случае) позорным неучем, то бабушки были бы дедушками etc.
no subject
Про спецификацию: она, конечно, бумажка и всё такое, но в ней есть места, которые могут быть интерпретированы вполне однозначно как требование определённой степени компиляции. Например: я выше в ответ
Кстати, Гвидо в момент создания питона уже семь лет был master of science и имел опыт языкотворчества.
no subject
удивительно, правда?
Lisp rules, all other programming languages sucks
Я тут подумал - как же нам всем остальным повезло что создатели XML не выбрали для него скобочную нотацию. Мысль о том что мы явно живем не в худшем из миров, отчасти примиряет с.
Re: Lisp rules, all other programming languages sucks
Re: Lisp rules, all other programming languages sucks
Re: Lisp rules, all other programming languages sucks
Re: Lisp rules, all other programming languages sucks
да, точно, про благостность закрывающих тагов я где-то читал.
правда, Грэм вряд ли писал что-то подобное даже после того как сошёл с ума.
Re: Lisp rules, all other programming languages sucks
Re: Lisp rules, all other programming languages sucks
совсем человек халтурно писать стал, по-моему.
Re: Lisp rules, all other programming languages sucks
Re: Lisp rules, all other programming languages sucks
(в английском языке не следует добавлять 's' к глаголам третьего лица множественного числа, только единственного. в прошлый раз я счёл это опечаткой).
Re: Lisp rules, all other programming languages sucks
А лисп тут совсем ни при чем, разумеется.
Re: Lisp rules, all other programming languages sucks
если Вам кажется что причём, то я весь уши.
no subject
no subject
no subject
no subject
no subject
, и в этом проявляется диалектический материализм..а егге с каждым разом все более мне жириновского напоминает, только не такой агрессивный.
no subject
А то пропадает человек.
no subject
Были классы -- чистые value objects. Просто структуры данных с конструкторами и аксесорами. Были классы -- чистые команды. Просто функции.
Но это внутри.
А сверху в качестве API выдавались обычные классы, совмещающие state и behavior.
Я спросил у тех мужиков:
- Вы с "Си" что ли портировали ?
- Нет, - говорят. С нуля так писали. Просто ООП мы тут не любим, но раз народу нравится -- сделали.