Join Yalantis and get a $1000 sign-in bonus! React.js, React Native, Python, Java, DevOps, BА. Apply now!
×Закрыть

Що нового у світі програмування

[Перевод на русский язык — в конце материала.]

Напевно не існує області знань, якій не пророкують «теплову смерть» від відсутності розвитку. Програмування не є виключенням: досить часто можна почути що все, що можна було придумати в програмуванні вже є у Lisp/Fortran/Smalltalk (підкреслити вибране) і нічого принципово нового з тих часів так і не створено

Мої улюблені контрприклади: прийоми програмування, що з`явились порівняно недавно, при цьому досить прості та з досить широким застосуванням, це

  • відкладені блоки в послідовності виконання
  • переміщення компіляції (staging)

Наука на службе земледелия
ілюстрація до процесу розвитку індустрії програмування взята з блогу Григорія Громова: abcdefgh.livejournal.com/1461405.html

Давайте подивимось на них детальніше:

Відкладені блоки в послідовності виконання команд

Приклад — конструкція scope в D або defer в Go. Основна ідея полягає в тому, що така одиниця комп’ютерної мови як блок виконання, розглядається не просто як послідовність команд з можливістю аварійного виходу за допомогою генерування виключень, але як більш складна конструкція, з кошиком куди в процесі виконання можна додавати команди, які будуть виконуватися згодом, після завершення основного блоку.

Таке структурування часто є більш зручним ніж традиційна обробка виключних ситуацій, внаслідок простішої композиції. Класичний приклад — нехай в нас є дві послідовні операції що потребують відновлення стану після збою:

Якщо ми захочемо додати сюди третю операцію, треба буде її вставляти у як ще один рівень вкладеності try/catch блоків. Якщо ми захочемо, щоб in та out були на одному рівні вкладеності, то виникне необхідність дублювання коду:

На D в такій ситуації можна написати наступне:

Тобто блок коду, маркований як scope(exit) буде виконуватися при виході з блоку (також в D існує scope(failure) та scope(success), що маркують виконання тільки у випадку відповідно неуспішного або успішного завершення блоку.

В Go приблизно з цією-ж метою додана конструкція defer:

Команди, що задані в defer будуть виконані наприкінці виходу із функції.

Як бачите, щоб додати ще одну операцію, що потребує відновлення, достатньо просто дописати її знизу. Тобто композиція виконання у даному випадку лінійна

Для подальшого знайомства з темою рекомендую

  • подивитись лекцію Александреску: channel9.msdn.com/...-Successful-Features-of-D , де є більш детальній опис та порівняння цієї технології з автоматичним менеджментом ресурсів у мовах подібних до С++,
  • також може бути цікава наступна стаття blog.golang.org/...er-panic-and-recover.html про обробку помилок в go.

Staging

Українською staging, напевно краще всього буде перекласти як ‘переміщення компіляції’. Ця технологія дозволяє надати одному і тому ж виразу різне значення, в залежності від контексту, де ми цей вираз використовуємо — тобто буквально перемістити компіляцію в інший контекст.

Тобто наступний вираз:

 
for(  s <- students if (s.course == 4) ) yield  s.name

може, в залежності від типу students компілюватися як в цикл, що проходить по колекції students, так і в щось подібне до sql запиту:

  select s.name from students s where s.course == 4

або виклику javascript функції:

Як це працює:

  • При компіляції мовні конструкції «віртуалізуються», тобто наведений вище приклад компілюється в щось подібне до students.filter(s => s.course == 4).map(s => s.name), а if (x) y else z в щось подібне до _ifThen(x,y,z) , де filter, map, _ifThen — стандартні функції або макроси.

  • Для стандартних типів компілятор використовує вбудовану реалізацію цих функцій (або макросів, як правило разом з inline оптимізацією, що повністю видаляє сліди цього прошарку), а для своїх типів користувач може підставити свої реалізації віртуальних конструкцій.

Ті з читачів, кто працює з C#, напевно впізнали LINQ; в scala деяке узагальнення цього підходу буде використовуватись як для роботи з базами даних, так і в ряді бібліотек (від компіляції javascript до хостингу обчислень на графічному процесорі)

Для подальшого знайомства з темою рекомендую

  • подивитись виступ Наді Амін (про побдуову в scala внутрішнього DSL для генерації Javascript: skillsmatter.com/...script-embedded-dsl-scala
  • та прочитати статтю Тьярка Ромпфа та Мартина Одескі:
    ‘Lightweight Modular Staging: A Pragmatic Approach to Runtime Code Generation and Compiled DSLs’ infoscience.epfl.ch/...47/files/gpce63-rompf.pdf де показано повний приклад побудови staging компіляції для невеликого внутрішнього DSL

Розкажіть мені щось

А які техніки програмування ви відкрили для себе за останні декілька років ? Якщо хочете доповісти щось цікаве — зв’яжіться зі мною та приходьте на Kyiv::fprog, що відбудеться 21-го липня 14:00 — 18:00 у конференц-залі кубік-центру на Шолуденка 3.
(реєстрація: dou.ua/calendar/2040 ). Приміщення спонсує strikead (мій поточний роботодавець, до речі організація з R&D центром у Києві, де активно використовується функціональний підхід), а наповнення у форматі міні-баркампу організують різні цікаві люди — точно буде секція про Erlang яку буде вести Лев Валкін (приводом для організації події став його приїзд до Києва) можливо також з перших рук можна буде почути також про використання Erlang в strikead від Олега Смірнова, обіцяє бути цікавою секція про LISP-похідні (Всеволод Демкін розповість про те, чому вам може не підійти clojure та як в Grammarly використовують комбінацію Java та Common Lisp для AI задач), ще два слота чекають на ваші теми.

Перевод на русский язык


👍НравитсяПонравилось0
В избранноеВ избранном0
Подписаться на автора
LinkedIn

Похожие статьи



Лучшие комментарии пропустить

Нарешті щось цікавіше, технічне. Насправді теми кризи аутсорсингу, стартапів, як пройти співбесіду та інші єресі трошки втомили.

142 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

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

— в языке Rust при отсутствии точки с запятой после последнего выражения возвращать его, в противном случае (если точка с запятой присутствует) — не возвращать. Подробнее было рассмотрено у Армина Ронахера lucumr.pocoo.org/...a-little-thing
— язык Haskell в целом не перестаёт удивлять, как минимум тот факт, что обе техники, описанные вами, можно запросто реализовать и они прекрасно впишутся в язык. Единственное, что придётся сделать — определить свои «==» и прочие стандартные функции (возможно назвать немного по другому, возможно использовать их явно в виде M.==, M.+, M.-). Собственно, как пример подобного есть в RealWorldHaskell в какой-то из глав, где показывают, как сделать свой тип, который будет вести себя как IO в одном случае, и как собственно написанные «Моки» в другом.

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

в сикпе это называлось “Propagation of Constraints”

Мартина Одескі:

виправте

Обратил внимание что статья о программировании на портале для программистов собрала реакций меньше чем статья про офисы, поэтому решил оставить комментарий.

конструкція scope в D або defer в Go

Еще в 50х годах в лиспе были возможности и пошире — макросы

Staging

Обычная монада, хотя тут нет сайдэффектов так что это скорее аппликативный функтор.

Еще в 50х годах в лиспе были возможности и пошире — макросы

макросы в лиспе появились только в 60-х и я не уверен, что они обладали теми же возможностями, что и сейчас.

Обычная монада, хотя тут нет сайдэффектов так что это скорее аппликативный функтор.

Это вы просто увидели параллели. Staging — это технология компилирования, а не фича языка

Где связь между сайд эффектами, и классификацией на монаду либо аппликативный функтор? оО

Обычная монада, хотя тут нет сайдэффектов так что это скорее аппликативный функтор.

Чушь какая-то.

...конструкція scope в D або defer в Go...
Еще в 50х годах в лиспе были возможности и пошире — макросы

в огороде бузина, а в Киеве дядька

Lisp рулит. Просто вставка

Елегантний підхід що вже юзається в мові Go то його також швидко заімплементили для С++ 0×11 ось тут blog.korfuri.fr/...o-defer-in-cpp — для повної досконалості лишилося тільки обгорнути все в макро з використанням __LINE__, щоб замість auto d2 = defer([=](){ delete[] buf; }) писати DEFER({ delete[] buf; })

auto in = File(“input”,"r");
scope(exit){ in.close(); }
auto out = File(“output”,"w");
scope(exit){ out.close(); }
out.copyFrom(in);

Наверное, я не оценил всю глубину глубин, но мне этот фрагмент напомнил инструкцию On Error из Visual Basic (msdn.microsoft.com/...v=vs.80).aspx)

Public Sub InitializeMatrix(ByVal Var1 As Object, ByVal Var2 As Object)
On Error GoTo ErrorHandler
' Insert code that might generate an error here
Exit Sub
ErrorHandler:
' Insert code to handle the error here
Resume Next
End Sub
Мощь! :))

А если переписать фрагмент в таком виде?

auto in = File(“input”,"r");
auto out = File(“output”,"w");
out.copyFrom(in);
scope(exit){ out.close(); }
scope(exit){ in.close(); }
На «антикварных» языках программирования вызов close() очутился бы в деструкторе класса File, который бы и вызвался при выходе объектов из области видимости, т.е. последние две строчки-финализатора вообще не надо было бы прописывать явно в блоке пользовательского кода.

Впрочем, если под занавес надо выполнять какой-то пространный танец с бубном, а не просто стандартное/типичное действие, то, наверное, потребность в finally-like инструкциях возникнет...

Кстати, в go есть panic, больше похожий на VBA onError ;)

// а вобще в колонке получилось неполное указание без иллюстрации — разница с ARM-подобными решениями появляется когда это логика восстановления нетривиальная, но в статье эти примеры не приведены, как слишком большие

идеи, вложенные в Smalltalk, были возвращены в каменный век из-за появления C++. «Этот язык был создан для программистов на C, и оказался ни рыбой ни мясом. И системы, которые мы теперь называем объектно-ориентированными, мало чем от него отличаются. Ни одна из них не объектно-ориентирована, если руководствоваться моим определением», — говорит Кэй.

«Раньше за переизобретение колеса давали по рукам, а сейчас заново изобретают проколотую шину».
blogs.computerra.ru/34195

Из последних нововведений в ЯП, я просматривал только С++.11.
Так там нету ничего кардинально нового, да, добавлены всякие вкусности, которые передраны с той же жавы или С#, ну и расширен stl.
В другие ЯП я давно не заглядывал (все никак не соберусь порыться в номом С#), но из того , что я слышал, то все новые фичи в ЯП можно разделить на 2 группы — фичи, относящиеся к функциональному программированию и к ООП. Насколько я понял, некоторые ЯП пытаются двигаться в обоих направлениях (типа питона).

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

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

а что с многопоточностью? В ленивых функциональных языках её реализуют на уровне компилятора, как я понимаю.

Питон двигается сразу во всех направлениях :) И процедурная парадигма, и ООП, и функциональная.

Если быть точным, то моя тема такая: «Почему вы можете (не) захотеть использовать Clojure в своем Java-проекте». Что касается использования Lisp’а в Grammarly, то об этом я немного рассказывал на последнем KyivAI по теме NLP (www.slideshare.net/...l-nlp-with-lisp ). Пока что добавить больше нечего

Как то тема зачем нужен лисп, кроме того что под него нету ntlk opennlp и тд, и нужно писать драйвера под вашу любимую бд, не раскрыта.

Ну, писать драйвера под БД не нужно: все уже украдено до нас.
А что касается раскрытия темы: приходите на наши встречи — там можно услышать больше информации и высказать свое фе, а еще лучше — выступить самому и таки раскрыть какую-нибуть тему ;)

Ну, писать драйвера под БД не нужно: все уже украдено до нас.

Ну канешн: github.com/...eloved/cl-redis

А что касается раскрытия темы: приходите на наши встречи — там можно услышать больше информации и высказать свое фе, а еще лучше — выступить самому и таки раскрыть какую-нибуть тему ;)

Так а че не обсудить здесь? Аудитория побольше будет.

Проекту cl-redis уже почти 3 года. Я сделал его практически сразу, как узнал о самом Редисе (когда тот был еще в 0-вых версиях). Как раз было свободное время и воспользовался моментом (т.е. тут скорее было больше моего желания успеть первым). А вам никогда не хотелось написать собственный драйвер БД? ;)

А так, давайте подискутируем. Могу еще раз ответить на предыдущий вопрос, хотя ответ на него есть в презентации: для меня это сочетание самого быстрого цикла обратной связи (хорошо для экспериментов) с возможностью писать декларативно (хорошо для результирующего кода). Ну и плюс это то, что результат довольно быстро работает, а если постараться, то может и очень быстро. А чем Лисп хорош именно для NLP (а не для исследований в целом) — это тем, что в NLP много работы с деревьями, а Lisp это поддерживает как нельзя лучше, вплоть до того, что деревья S-выражений изоморфны деревьям грамматического разбора.

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

Проекту cl-redis уже почти 3 года. Я сделал его практически сразу, как узнал о самом Редисе (когда тот был еще в 0-вых версиях). Как раз было свободное время и воспользовался моментом (т.е. тут скорее было больше моего желания успеть первым).

Ок, теперь у тебя есть еще время(причем лет 5 уже как) воспользоваться моментом и написать драйвер для моей любимой базы HBase(желательно с поддержкой map reduce).

А вам никогда не хотелось написать собственный драйвер БД? ;)

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

что деревья S-выражений изоморфны деревьям грамматического разбора

Мне кажется либу для парсинга s expressions написать на 3 порядка проще чем драйвер к БД или враппер клиент к ntlk.

Я вообще к чему всю эту тему завел, я в лиспе чесно говоря не сильно разбираюсь, на уровне факториалов и хелло ворлдов. Поэтому что бы понять действительно ли лисп продуктивнее я смотрю чужой код, например: shootout.alioth.debian.org/u32/lisp.php и вот оказывается что лисп требует писать больше буков чем многословная джава, т.е. он вообще не конкурент например скале с ее батарейками, и при этом теряет в производительности и экосистеме библиотек и непонятно в чем профит. Мне было бы интереснее посмотреть решения конкретных задач на лисп, которые с трудом решаются на джаве. s expressions как я уже написал не катит.

Во-первых, на ты мы пока еще не переходили. Ты бы сначала набрался смелости подписаться от своего имени.

Что касается HBase, то пока не было необходимости с ней работать, так что вопрос не ко мне.

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

Деливерить — это в смысле рожать?

Мне кажется либу для парсинга s expressions написать на 3 порядка проще чем драйвер к БД или враппер клиент к ntlk.

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

Что касается написания враппера к nltk, то в этом нет необходимости. Во-первых, потому, что если делать что-то серьезное, то nltk, как и любая другая библиотека (кстати, для Лиспа есть nlp-библиотека langutils, ссылка на которую я дал в презентации) не даст исчерпывающего и самого лучшего во всех сферах на данный момент решения. В лучшем случае, удастся взять кусок из одной библиотеки, кусок из другой. Во-вторых, потому что написать простенький сервер, который будет слушать запросы через сокет, скармливать их парсеру из nltk, или же opennlp, или же какому-нибудь мало известному, но самому быстрому, написанному на, скажем, С++,— дело пары дней работы. Да еще и +10 к scalability.

Я вообще к чему всю эту тему завел, я в лиспе чесно говоря не сильно разбираюсь, на уровне факториалов и хелло ворлдов. Поэтому что бы понять действительно ли лисп продуктивнее я смотрю чужой код, например: shootout.alioth.debian.org/u32/lisp.php и вот оказывается что лисп требует писать больше буков чем многословная джава, т.е. он вообще не конкурент например скале с ее батарейками, и при этом теряет в производительности и экосистеме библиотек и непонятно в чем профит. Мне было бы интереснее посмотреть решения конкретных задач на лисп, которые с трудом решаются на джаве. s expressions как я уже написал не катит.

Debian Shootout — это, конечно, самое прикольное место, где можно почитать код на разных языках, но не самое показательное, скорее наоборот. Но если интересно в этом покопаться больше, вот хорошая ссылка: swizard.livejournal.com/159125.html
На счет скалы и т.п. — вперед и с песней, как я написал выше, у каждого свои причины выбора того или иного языка. Если причина — наличие библиотек, то, наверно, Лисп не самый лучший вариант. Хотя мне лично библиотек хватает. Что касается того, чтобы писать больше букв: ну, можно сравнить тот же cl-redis и Jedis (github.com/...xetorthio/jedis) — редис клиент на джаве, который я использую. Хотя, конечно, это не менее глупо, чем Alioth Shootout, поскольку кроме размера имеют значение (как правило, намного большее) еще многие другие вещи.

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

Во-первых, на ты мы пока еще не переходили.

Я тут со всеми на ты по разным причинам.

Ты бы сначала набрался смелости подписаться от своего имени.

Зачем? В чем профит?

Что касается HBase, то пока не было необходимости с ней работать, так что вопрос не ко мне.

Это просто был наглядный пример отсуствия драйвера к БД, и например поддержки map reduce написать будет очень нетривиально.

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

Ну вот предположим я нагенерировал с помощью antlr дерево разбора s expressions в джаве, чем это хуже лиспа?

Во-вторых, потому что написать простенький сервер, который будет слушать запросы через сокет, скармливать их парсеру из nltk, или же opennlp, или же какому-нибудь мало известному, но самому быстрому, написанному на, скажем, С++,— дело пары дней работы.

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

Да еще и +10 к scalability.

Ага, и −100 к latency и throughput.

Но если интересно в этом покопаться больше, вот хорошая ссылка: swizard.livejournal.com/159125.html

Я не понял к чему эта ссылка, здесь обсуждается продуктивность языка, а ссылка о производительности. К тому же в джаве тоже вполне можно сделать threads affinity, и тогда она наверняка опять уделает лисп.

Что касается того, чтобы писать больше букв: ну, можно сравнить тот же cl-redis и Jedis (github.com/...xetorthio/jedis) — редис клиент на джаве, который я использую.

Утверждается что твоя либа матчит по фичам jedis?

Деливерить — это в смысле рожать?

Я еще не пропустил слово лопата?

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

досвидания, у тебя тоже ДЗ нарисовалось — поработать над качеством аргументов.

Я тут со всеми на ты по разным причинам.

Чего-то мне кажется, что причина одна — врожденное хамство.

Это просто был наглядный пример отсуствия драйвера к БД, и например поддержки map reduce написать будет очень нетривиально.

А можно список клиентов HBase на разных языках? А то я чего-то не нашел. Что касается MapReduce, то это ж Hadoop делает, причем тут HBase?

Утверждается что твоя либа матчит по фичам jedis?

В принципе, по некоторым даже превосходит.

досвидания, у тебя тоже ДЗ нарисовалось — поработать над качеством аргументов.

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

Чего-то мне кажется, что причина одна — врожденное хамство.

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

А можно список клиентов HBase на разных языках? А то я чего-то не нашел.

Есть dsl для groovy, scala, есть либа для питона(github.com/...lster/happybase) остальным я не интересуюсь.

Что касается MapReduce, то это ж Hadoop делает, причем тут HBase?

При том что если ты вдруг захочешь в своей map не сканить всю таблицу а сканить только данные которые тебя интересуют тебе прийдется юзать hbase specific api.

На таком уровне дискуссии в этом нет особого смысла.

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

>> К тому же в джаве тоже вполне можно сделать threads affinity, и тогда она наверняка опять уделает лисп.

На сколько я знаю, в java нельзя сделать thread affinity. Если я ошибаюсь — не могли бы Вы показать как именно это можно сделать?

интересно что оба примера показывают что низя ни там ни там:

Вот тут вызывается то что на Lispe было написано?:
extern-alien «pthread_setaffinity_np»
(function int
unsigned-long
unsigned-long
(* unsigned-char)))
(sb-thread::thread-os-thread thread)
16
(cast alien-cpuset (* unsigned-char)))

Как и написано: исходник биндингов к pthread_*affinity

А вот это:
import com.sun.jna.*;

import com.sun.jna.ptr.LongByReference;

зачем JNA в чистой Java?
Чтобы:

final CLibrary lib = CLibrary.INSTANCE

Итого:

Оба примера показывают что ни на LISPe, ни на Java нельзя сделать написать код для thread affinity.

Хотя вопрос сложней, если ОСь параноидальная, то и код на ассемблере не даст обойти ее таск шедулер.

Т.е. код который я привел не на джаве а на брейнфаке. Как скажешь

Приведи код CLibrary на джаве, умник.

В приведенном мной коде юзается posix api который понятное дело у каждой операционки свой. Для тебя сюрприз что джава дергает системные АПИ?

Для меня сюрпризом является возможность транслятора подключать внешний двоичный код — свойством языка программирования.

Давайте ка поищем на TIOBE языки которые этого не умеют. JavaScript разве что. И то, надо бы JNode копнуть.

Но хорошо, и исправляюсь

thread affinity — можно делать и на Java и на LISPe, и почти на всех остальных языках промышленного программирования. Плевое дело — написал на Си либу, и вызвал ее.

Нахера либу писать если она уже встроена в ОС? Для открытия файлов ты тоже либы пишешь?

А какая разница она встроена в ОС или написана лично для ответа на вопрос:

Какое отношение имеет возможность вызова внешнего двоичного кода к свойствам, возможностям языка программирования?

Для открытия файлов ты тоже либы пишешь?

Я не объявляю свойством или фичей ЯП — умение открывать файлы.

Я не объявляю свойством или фичей ЯП — умение открывать файлы.

Так че терь если я в джаве или питоне открываю файл это я уже не на джаве пишу? На джаве открывать файлы нельзя? Мне твои терминологические притензии кажутся дeбильными.

Мне твои терминологические притензии кажутся дeбильными.

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

Давай на секундочку отвлечемся от твоего любимого занятия демагогией и вернемся к моему вопросу: в джаве и питоне файлы открывать можно?

ты идиoт?

Нет, но думаю что вот ты идиoт.

По заданному вопросу — явно не все в порядке у тя с головой :)

Та нет, это у тебя пипец с головой, причем по большинству вопросов.

Ну если для тебя это комплимент, то все еще хуже чем я думал.

От тебя — комплимент.

Ты оказывается меня читаешь, постоянно ни асиливаешь, тебя это бесит, иначе бы не писал в ответ — как не считать комплиментом оскорбления от такого? Всегда от хм... недалеких считаю за комплимент и «демагогию», и прямое хамство и пацанские провокации :)

Мне твоя позиция кажется странной, но люди конечно все разные.

А мне твоя — нет :)

Типическая :)

Люди все же одинаковые. Даже я — в большинстве — ничуть не оригинален.

В приведенном мной коде юзается posix api который понятное дело у каждой операционки свой. Для тебя сюрприз что джава дергает системные АПИ?

Как это API POSIX у каждого свой? POSIX он та и POSIX, чтобы быть одинаковым везде. Если говорить, что thread affinity поддерживается практически нативно, через вызов системных функций, то получается, что под каждую host систему надо использовать свой кусок кода для вызова системной необходимой системной функции.

А что делать если оно запущено на какой-то убогой платформе, где нет такой возможности? Правильно, ничего не делать, поэтому нативно thread affinity в Java нет, чтобы не сужать возможность её применения.

Или, например, что будет, если java на определённой хост платформе не использует системные потоки, соответственно и вызов для установки affinity будет бесполезен.

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

Я считаю, что да, на java нельзя устанавливать affinity mask.

Что будет в системах, где для java потоков используются несистемные потоки?

Что будет в системах, где используются LWP?

Что будет в системах где нельзя менеджить потоки, а только процессы? Например, Solaris, где affinity можно выставить извне на процесс, но не на поток.

Что будет в системах типа Sun Sparc, где в горячую можно вытаскивать и добавлять процессоры или в горячую конфигурировать аппаратный домен и динамически распределять ресурсы?

Так а файлы, файлы можно в джаве открывать? Вдруг в системе нету диска?

И что делает код выше если не устанавливает affinity mask?

Так а файлы, файлы можно в джаве открывать? Вдруг в системе нету диска?

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

И что делает код выше если не устанавливает affinity mask?

А если твоё приложеннице было запущено на Solaris под Sparc’ом, что будет в результате?

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

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

А если твоё приложеннице было запущено на Solaris под Sparc’ом, что будет в результате?

Ничего, на соларисе такого АПИ нету, и скорее всего CLibrary не содержит такого метода. А почему ты отвечаешь вопросом на вопрос?

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

Дело в том, что не имея возможности открыть себя, даже из участка memory mapped памяти или zip архива java приложение стухнет на этапе загрузки. Файлы, как ты выражается — это mandatory для java, их функциональность обеспечивается либо самой виртуальной машиной, либо JNI прослойкой самой машины, без неё оно работать не будет вообще. Грубо говоря, при переносе джава машины на новую хост платформу ты обязан реализовать работу с «файлами», даже если интерфейс нестандартен (могу сказать за PhoneME и J9, т.к. я их портировал на новые платформы). Это стандарт, описанный в спецификации.

Ничего, на соларисе такого АПИ нету, и скорее всего CLibrary не содержит такого метода.

Ну хорошо, тогда возьмём IBM J9, под линуксе у неё не будет тоже такого метода. Всё, что выходит за рамки

спецификаций Java ME, SE, EE — не является поддерживаемым самой джавой, это не джава. Это сторонее приложение или библиотека, которое предназначено для работы на конкретной целевой платформе, не более того.

А почему ты отвечаешь вопросом на вопрос?

А почему ты спрашиваешь?

Дело в том, что не имея возможности открыть себя, даже из участка memory mapped памяти или zip архива java приложение стухнет на этапе загрузки. Файлы, как ты выражается — это mandatory для java, их функциональность обеспечивается либо самой виртуальной машиной, либо JNI прослойкой самой машины, без неё оно работать не будет вообще. Грубо говоря, при переносе джава машины на новую хост платформу ты обязан реализовать работу с «файлами», даже если интерфейс нестандартен (могу сказать за PhoneME и J9, т.к. я их портировал на новые платформы). Это стандарт,

Замечательно, это очень интересно, только совсем не проливает света как правильно нужно говорить что именно делает выше приведенная программа которая таки устанавливает affinity маску для потоков, а так же как правильно описывать процесс отлрытия файлов в джаве. Спор ведь об этом?

Всё, что выходит за рамки

спецификаций Java ME, SE, EE — не является поддерживаемым самой джавой, это не джава.

Я не согласен с такой постановкой, т.к. например тогда окажется что под андроид програмят тоже не на джаве, и проект harmony который не сертифицировали тоже по чисто бюрократическим причинам тоже не джава

А почему ты спрашиваешь?

Очевидно потому что хочу узнать причину по которой ты уходишь от ответа на прямой вопрос, задавая новые. Интересно узнаю?

а так же как правильно описывать процесс отлрытия файлов в джаве. Спор ведь об этом?

Ты споришь со всем, что я пишу. Файл — это абстрактная сущность в java, за ней может стоять всё, что угодно. Вот почему твой вопрос, а что, если не будет диска звучит чрезвычайно глупо.

только совсем не проливает света как правильно нужно говорить что именно делает выше приведенная программа которая таки устанавливает affinity маску для потоков

Я тебе привёл пять примеров, где твоя программа этого не делает. Она не устанавливает маску для потоков, она дёргает специфический сискол.

Я не согласен с такой постановкой, т.к. например тогда окажется что под андроид програмят тоже не на джаве, и проект harmony который не сертифицировали тоже по чисто бюрократическим причинам тоже не джава

Ты чувствуешь разницу между сертификацией и реализации согласно спецификации? В проекте harmony разве есть класс HarmonyFile вместо описанного спецификацией File?

Ладно, я устал спорить с твоим бредом. Всего хорошего.

Ты устал спорить сам с собой, dude!

Как это API POSIX у каждого свой?

я оговорился, я хотел сказать — код свой. А АПИ конечно же общий. Хотя sched_setaffinity это на самом деле linux specific api

Кстати, помню в жабу хотели автоматик ресурс менеджмент добавить. Так что деффер особо то и не нужен. Не помню в 7 или 8? Добавили, недобавили ?

Не помню в 7 или 8? Добавили, недобавили ?

В 7 уже должно быть.

Но дефер — это, как по мне, больше колбэки, более гибкий механизм.

Ну дык для колбеков — есть колбеки.
Ибо ресурс менеджмент — да, annoying; а использования дефера для чегото эдакого (вместо провославных коллбеков) — гкод на ровном месте. Мое сугубо неграмотное ИМХО.

Всё уже было в Симпс^W Лиспе.

может сюда можно отнетси также АОП?

пожалуй да, появился сравнительно недавно. Как-то не подумал потому что в java-мире все о нем знают ;)

Вот интересная вещица, до которой руки никак не дойдут пощупать:

What is Qi4j™?

The short answer is that Qi4j™ is a framework for domain centric application development, including evolved concepts from AOP, DI and DDD.

Qi4j™ is an implementation of Composite Oriented Programming, using the standard Java 5 platform, without the use of any pre-processors or new language elements. Everything you know from Java 5 still applies and you can leverage both your experience and toolkits to become more productive with Composite Oriented Programming today.

www.qi4j.org/index.html

Похоже многие недовольны текущим состоянием дел в ООП и в частности в java. Вот еще пример: www.eclipse.org/objectteams.

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

Недовольство, неудовлетворенность — это вообще признак ищущего ума (если хроническое нытье — то пора обратится к психотерапевту или йоге)

Оффтоп

Рекорд скорости на суше в 100 км/ч взял электромобиль названный автором «Всегда недовольный» (1899)

А относительно недавно это сколько? 10 лет?

Вроде в средине 90-х, более ранних статей не видел.

Ну если расширить термин «недавно» до 15-и лет то очень много наизобретали за это время.

то очень много наизобретали за это время.

Список в студию.

Ну блин, ioc контейнеры, ajax и все что с ним связано, map reduce for big data, orm, nosql в современном понимании.

вооооот.

А статья то о новом в языках программирования :)

АОП так же релевантен языкам программирования как и IoC & ORM.

Ну нарешті! Технічний допис :) Дуже дякую і сподіваюсь, що тепер можна буде заходити на DOU частіше.

Маленька ремарка: придати -> надати :)

Також «перевести» — «перекласти», «Київа» — «Києва».

я, честно говоря, не совсем понял, зачем вводить в язык дополнительные ключевые слова для defer/scope.

в языках, где есть деструктор, гарантированно вызываемый при выходе из блока, чистка ресурсов будет произведена автоматически; если нужна какая-то особая логика, все это легко перекладывается на деструкторы же, например

auto guard = std::shared_ptr<std::ifstream>(&file, std::mem_fn(&std::ifstream::close));

если в языке есть только (недетерминированный) сборщик мусора, задача решается, в простейшем случае, конструкциями типа with-* или bracket — в общем, как и во многих других местах, на свет божий просятся монады и аппликативные функторы :)

иными словами, мне кажется, что большинство современных языков достаточно мощны, чтобы требовать какого-то особого синтаксиса для defer/scope. более того, я считаю, что разнесение логики создания и инициализации объекта и логики его чистки — вещь спорная, если не сказать вредная. пример с go это иллюстрирует: мы сначала создаем объект, потом производим какие-то действия (блок if), а потом вдруг спохватываемся — ах, да, если файл таки открылся, нужно не забыть его закрыть при выходе. я понимаю, что в данном случае это особенности go, но все-таки..

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

идея как понял — шире просто выхода из блока.

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

defer в Go вроде да, только для ухода от вложенности блоков для обработки исключительной ситуации. Приятная, но мелочь.

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

или даже аппликативными функторами

но не для ФЯ, а для — ООЯ.

обычной монадой

монадой

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

Но нет уж, если в руках монада, то всё вокруг выглядит теорией категорий.

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

Но нет уж, если в руках монада, то всё вокруг выглядит теорией категорий.

уверяю вас, что то, что вы напишете с помощью «первоклассных функций» (наверное, в данном случае все-таки лямбд), будет монадой, даже если вы об этом не подозреваете

«первоклассных функций» (наверное, все-таки лямбд),

en.wikipedia.org/...-class_function

Мне было лень искать правильный перевод, но я думаю, все меня поняли.

будет монадой, даже если вы об этом не подозреваете

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

edited: Поубрал фамильярности, LOR пагубно влияет.

хмм. я считаю, что знание (или, в данном случае, незнание) теории все-таки влияет на практику.

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

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

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

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

Хоть я и не о том писал, но с тобою соглашусь.

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

Также мне не очевидно почему селект это монада, дайте ссылку на почитать.

Ссылки на почитать нету, на уровне моей некомопетенции это ясно и так. Если видишь какие то проблемы, то могу попытаться формально рассписать.

Опять про модные монады
ko.com.ua/...ye_monady_42237
Собственно, вот и вся суть монад, будь они неладны.

Вот и вся тайна.

Статья не проливает свет на то зачем нужен monadic_object, почему нельзя опирировать оригинальными типами.

Ну и аппликативные функторы как то тоже не задеты ))

Статья показывает суть.
А все остальное умничание — это профессиональный жаргон.
Пример:

Монада — это очень просто. Это такой эндофунктор M вместе с двумя естественными преобразованиями i:1->M и m:M^2->M, удовлетворяющими паре очевидных соотношений: m_Xi_{MX} = 1 = m_XM(i_X) и m_Xm_{MX}=m_X(Mm_X). Всегда получается из пары сопряжённых функторов, вообще говоря, не единственным образом. Ну, а в ФП монады — те же самые, только в категории типов данного языка.

Из-за которого: «Каждый программист на Haskell считает своим долгом написать руководство по монадам»

... аппликативные функторы ...

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

форумное общение

А все остальное умничание — это профессиональный жаргон.

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

Я вижу на твой взгляд в форумном общении главное раздуть щеки

То есть по сути моего уточнения возражений нет.

а как кому видится мой взгляд — это и мне неинтересно.

Уточнения настолько абстрактны и необоснованы что я не заморачивался на возражения

знание (или, в данном случае, незнание) теории все-таки влияет на практику.

Вот интересно, а все ли архитекторы и дт строители знают квантовую физику, при расчетах для построения домов используют расчеты на с использованием «закона всемирного тяготения» или формул которые из него получаются (при том так в 100500-м поколении)?

А вы когда розетку дома ремонтируете (о теме про розетку) — закон ома применяете ?

Разница с ARM начинается когда restore логика появляется — в лекции Александреску сравнение с С++ где-то на 4-ой минуте начинается

потом производим какие-то действия (блок if), а потом вдруг спохватываемся — ах, да, если файл таки открылся, нужно не забыть его закрыть при выходе

А как твой брекет отработает если файл не открылся?

В языках программирования действительно тупик. Не в программировании, это, ИМХО, шире понятие. Приведенные же примеры как раз из нового в ЯП.

Идет просто шлифовка мелочей и гибридизация.

Делегаты — это развитие идеи указателей на функции, с учетом ООП
LINQ — проникновение идей ФП в императивные ЯП. Декларативности в императивное окружение.

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

Многопоточное программирование — тут интересней: считать ли синтаксический сахар, повышающий удобство и безопасность — новым?

Примеры же интересные.

scope в D або defer в Go.

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

Со Staging — не понял. Программисту вообще говоря должно быть все равно во что и как оно там компилируется. Главное — семантика соблюдена или нет.

То есть это новое в подходе к написанию трансляторов?

P.S.

Относительно новым мне кажется аспектное программирование, хотя тоже...

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

Ну например — семантика(смысл) паттерна стратегия известна. Но синтаксически она не воплощена в ЯП. Просто макросом — вряд ли возможно охватить. DSL — как бы не вышел слишком тяжел.

То есть ближайшим действительно новым в ЯП должно быть появление дженериков для алгоритмов, а не для типов.

То есть это новое в подходе к написанию трансляторов?

это как-бы трансляция языка на котором пишешь в гостевой для обращения к гостевой системе (for быстро скомпилировался в запрос который выполнился и вернулся назад). То есть пользуются этим из прикладной программы а не из компилятора

появление дженериков для алгоритмов,

... FP ?

это как-бы трансляция языка на котором пишешь в гостевой для обращения к гостевой системе
То есть подход как предлагался лингвистами для машинного перевода:
создается некий универсальный язык и переводчики на него и с него?

Не на уровне бинарного кода, как в LLVM, JVM, CLR, а преобразование синтаксических конструкций одного языка, в другой?

Или это развитие идеи метапрограммирования?

... FP ?
Там да, есть подобное. Но там «дженерики для алгоритмов» являются естественным следствием самой парадигмы. А вот в другой, императивной парадигме — возможно ли вообще такое? в LISPе и Nemerle как понимаю предпринимаются такие попытки
То есть подход как предлагался лингвистами для машинного перевода:

создается некий универсальный язык и переводчики на него и с него?

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

// А универсальный язык — это аналог той идеи, что у нас была с универсальными с UML моделями

Понятно.

Да, неплоха задумка, вместо создания языково, платформенного независимого API — переложить это дело на транслятор, а он уж пусть сам разбирается.

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

Відкладені блоки в послідовності виконання команд

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

Вот перечитал пару раз, но так и не понял

відкладені блоки в послідовності виконання

Чем это не колбэки?

переміщення компіляції (staging)

Чем это не трансляция?

Так шо пока

було придумати в програмуванні вже є у Lisp/Fortran/Smalltalk (підкреслити вибране) і нічого принципово нового з тих часів так і не створено

Чем это не колбэки?

— ну как, можно сказать, что это прием, сводящийся к синтаксическому сахару для пристобачивании callback к концу выполняемого блока [изложение упрощенное] — но он же от этого самостоятельным приемом быть не перестанет

Чем это не трансляция?

— тут непонятно,

Чем это не трансляция?
— тут непонятно,
Я не совсем понял, чем «Staging» принципиально отличаетсо от трансляции кода написанного на одном языке в код на другом, в презентации вроде из Скалы в ДжС.

тем что это происходит в обычном компиляторе и встроенно туда *библиотекой в комилируемой программе*.

Ну яким боком там колбеки?

Зразу дам правильну відповідь - ніяким!

Зразу дам правильну відповідь — ніяким!
Все в этом имре относительно :)

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

а как назвать «выполнить код дето в другом месте» ?

Из того что я для себя действительно открыл- для это был просто потерянный пласт знаний — computer science. CS в том виде в котором он преподаётся на западе: алгоритмы структуры данных, математические выкладки по выше перечисленному. Так же обновил свои знания по функциональному программированию — но остался поклонником ООП

Очепятка в последнем предложении? /closure/clojure/

да :( Попрошу подправить из jango — админки

Так получилось что всё, что я узнал это всё было новым и единственным. А узнавать я начал с net 3.5 то есть С# и совсем недавно. Но некоторые вещи меня поразили своей глубиной проработки и сложнопредставляемыми границами использования.

Это
Делегаты — по сути ссылка на метод (или объявление сигнатуры метода), причём как на метод экземляра так и статический;
Обобщения — различные сопсобы безопасного типанезависимого программирования, например класс который будет осуществлять сортировку строк, целых, других классов и тд. Для строго типизированного C# очень важная возможность.
LINQ — синтаксический сахар для работы с коллекциями. Иногда просто срывает башню от простоты с которой можно сделать неимоверно сложный выбор из коллекции.
Рефлексия — получение сведений о типах в процессе выполнения, что даёт возможность использовать любой код не доступный во время компиляции. Наверное круто для net, как в других языках не знаю.

Многопоточное программирование — тут без комментариев.

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

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

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

И что здесь, простите, нового или уникального (кроме ЛИНКа)?

Я представляю насколько изматывающим было программирование без этого инструментария.

Нормально всё было. Таки надо обучение программированию начинать с си...

Ты хоть иногда корону снимай.

Ты хоть иногда слушай, что тебе говорят. Топик был про необычные средства, вроде скопов из D. А то что ты написал, тянется ещё Java (кроме LINQa), и всё кроме LINQa и рефлексии было в С++. Сто лет в обед этим вундервафлям. Я уже не говорю о том, насколько акуратно и осторожно их надо использовать, если не хочешь, что бы твой сайт давал 100% нагрузку на сервак при 100 юзерах.

Нарешті щось цікавіше, технічне. Насправді теми кризи аутсорсингу, стартапів, як пройти співбесіду та інші єресі трошки втомили.

Что-то мне в «gistfile1.js» не нравитсо.

UPD. Вроде оно не эквивалентно

select s.name from students s where s.course == 4

Берет не значения, а

s.course == 4

s — это ключ, для массива индекс

Точно. Спасибо, исправил ;)

Спасибо, исправил ;)
Данепохоже
retval.add(s.name)
;)

Вопрос:
Этот код генеренный или просто пример «если бы оно генерировало»?

как-бы (похожий пример генеренный есть в лекции Амин, отличается тем что переменные называются s1, s2, ... )

Тада, похожу по ссылкам, мо прояснитсо.

самый полезный и интересный технический пост, который я видел за последнее время на DOU :)

самый полезный и интересный технический пост

Решили занятсо маркетингом?

«самый полезный и интересный» == «единственный»

Решили занятсо маркетингом?
С каких пор? Был замечен за этим?

Не единственный, был уже один у колумнистов.

Не единственный, был уже один у колумнистов.

А ссылочку?

Просмотрел первую страницу dou.ua/columns там не только «под рукой», там ваапшэ нет

за техн. статьями это на хабр)) попытка хороша, вот только ашеров то больше)

попытка хороша, вот только ашеров то больше)

не понял

Подписаться на комментарии