А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: LaPerouse  
Дата: 21.12.12 10:42
Оценка: 16 (3) +1
В продолжение темы Языки общего назначения не имеют смысла
Автор: WolfHound
Дата: 07.04.12


Wolf Hound утверждает, что разработка DSL для некоторой задачи и последующее ее решение на получившемся языке почти всегда более оправдано, нежели использование языка общего назначения. В качестве примера он приводит программу, текст которой на языке общего назначения (Java) занимает 56 листов, а на "DSL" — 17 строк. Ссылки — ниже, откройте и просмотрите хотя бы начало:
http://suif.stanford.edu/~jwhaley/PLDITutorial.ppt
http://bddbddb.sourceforge.net/index.html

Секрет прост — java-программу переписали на принципиально другом языке программирования (Datalog) с совершенной иной парадигмой и вычислительной семантикой. Разница между java и Datalog-ом во много раз превосходит разницу между явой-же и к примеру, хаскелем. Да что там говорить — с точки зрения Datalog, языка сверхвысокого уровня, haskell и java — примерно одно и то же. Вот откуда растут ноги ошеломляющей разницы между 56 страницами и 17 строками.

Но:
1. Возможность подобного радикального сокращения существует только для задач, которые изначально плохо ложатся на выбранный императивный (объектно-ориентированный или функциональный) язык общего назначения. Но и задач, в которых оправдано использование декларативного языка (такого, как Datalog), совсем немного. По правде говоря, в практике обычного программиста их почти нет. Таким образом выгоды от использования декларативных dsl-ей, мягко говоря, преувеличены. Их просто негде применить. Неудивительно, что сторонники DSL "не поняли" код, предложенный AndrewVK в качестве примера для демонстрации мощи DSL-я. А секрет прост — Андрей предложил чисто императивный код, который не перепишешь на том же даталоге.
2. Спрашивается, при чем же здесь собственно сам DSL? Эдак ведь и тот же хаскель можно считать DSL-ем, да и вообще любой язык, отличный от языка реализации.
3. Пикантность ситуации в том, что DSL-и таки повсеместно используются в индустрии. Та же объектно-ориентированная модель предметной области — пример DSL-я (а точнее, его ядра), массово используемого на практике. И в приведенном Андреем коде уже используется DSL, составленный из модели инвентарных карточек и структур языка ОН для манипулирования ею. Модель предметной области может быть легко сгенерирована из UML-подобного представления. Я в последнее время, к примеру, описываю модели исключительно на EMF.
Социализм — это власть трудящихся и централизованная плановая экономика.
Re: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 12:07
Оценка:
Здравствуйте, LaPerouse, Вы писали:

LP>Но и задач, в которых оправдано использование декларативного языка (такого, как Datalog), совсем немного. По правде говоря, в практике обычного программиста их почти нет. Таким образом выгоды от использования декларативных dsl-ей, мягко говоря, преувеличены. Их просто негде применить. Неудивительно, что сторонники DSL "не поняли" код, предложенный AndrewVK в качестве примера для демонстрации мощи DSL-я. А секрет прост — Андрей предложил чисто императивный код, который не перепишешь на том же даталоге.


С одной стороны +1, с другой — мне не нравится формулировка "Андрей предложил императивный код". Я ту ветку не читал (точнее, читал, но не всю), но ИМХО надо предлагать не код, который нужно переписать, а задачу, которую нужно решить. Пример: тут неподалёку в рассуждениях про anemic domain model Синклер писал
Автор: Sinclair
Дата: 19.12.12
следующее:

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


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

Кроме того, на той же скале DSL-и часто используются в императивном коде для компактного решения всяких рутинных задач. К примеру, combinator parsers, позволяющий писать программы разбора грамматик прямо в синтаксисе BNF (и в отличие от всяких yacc/bison да javacc, тут не требуется промежуточный этап генерации исходного кода). Или запуск процессов.
Re[2]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 12:10
Оценка:
D>ИМХО надо предлагать не код, который нужно переписать, а задачу, которую нужно решить.

Ну и, разумеется, довольно глупо считать DSL-и очередной серебряной пулей, применимой к любым задачам.
Re[2]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: LaPerouse  
Дата: 21.12.12 13:17
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, LaPerouse, Вы писали:


LP>>Но и задач, в которых оправдано использование декларативного языка (такого, как Datalog), совсем немного. По правде говоря, в практике обычного программиста их почти нет. Таким образом выгоды от использования декларативных dsl-ей, мягко говоря, преувеличены. Их просто негде применить. Неудивительно, что сторонники DSL "не поняли" код, предложенный AndrewVK в качестве примера для демонстрации мощи DSL-я. А секрет прост — Андрей предложил чисто императивный код, который не перепишешь на том же даталоге.


D>С одной стороны +1, с другой — мне не нравится формулировка "Андрей предложил императивный код". Я ту ветку не читал (точнее, читал, но не всю), но ИМХО надо предлагать не код, который нужно переписать, а задачу, которую нужно решить. Пример: тут неподалёку в рассуждениях про anemic domain model Синклер писал
Автор: Sinclair
Дата: 19.12.12
следующее:


К сожалению, от современных реалий не получится оторваться. Вот тот же самый Datalog можно использовать гораздо шире, чем он используется сегодня (а в бизнесе он не используется чуть менее чем нигде). Причем есть очень много случаев, когда применение Datalog-подобных сверхвысоких языков в качестве языка запроса (вместо SQL) способно радикально упростить реализацию (почти как в приведенном случае — 56 страниц против 17 строк). Говорю не просто так — использовал сам Prolog для денормализации структуры бд. На нем действительно много можно сделать радикально проще, чем на SQL или императивном языке (объектно-ориентированном или функциональном). Но не всегда можно использовать оптимальный инструмент для задачи. Нужно исходить из реалий — сегодняшний мейнстрим, это SQL и/или объектно-реляционные средства с процедурными алгоритмами обработки. Как здесь может помочь DSL?
Социализм — это власть трудящихся и централизованная плановая экономика.
Re[3]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 13:28
Оценка:
Здравствуйте, LaPerouse, Вы писали:

LP>Нужно исходить из реалий — сегодняшний мейнстрим, это SQL и/или объектно-реляционные средства с процедурными алгоритмами обработки. Как здесь может помочь DSL?


Ну как я себе вижу типичную эволюцию проекта: если обнаруживаются повторяющиеся куски кода, они выносятся в повторно используемые функции. А если обнаруживаются повторяющиеся паттерны, реализованные разными людьми вразнобой, их реализации приводятся к общему виду, и тут возникает возможность тоже что-нибудь да подсократить, введя либо вспомогательное API, либо каркасное (в т.ч. макросы). А это уже можно рассматривать как DSL, даже если он не выглядит как "L" (откровенно говоря, помнить синтаксис настоящих "L" — так себе радость). Такие ситуации на больших проектах — на каждом шагу, и всё лучше прошивать паттерны в код, чтобы никто не смог сделать поперёк, чем писать многочисленные гайдлайны "а вот это делаем так-то", которые далеко не всегда помнят даже их авторы, а остальные так и вообще не читают.
Re[3]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 13:32
Оценка:
Здравствуйте, LaPerouse, Вы писали:

LP>Нужно исходить из реалий — сегодняшний мейнстрим, это SQL и/или объектно-реляционные средства с процедурными алгоритмами обработки. Как здесь может помочь DSL?


О, кстати! Про слона-то я и забыл! LINQ — это ж тоже DSL (по крайней мере, я так слышал, его к C# присобачили как захардкоженное синтаксическое расширение). А его аналоги на scala — и подавно DSL-и. Raw string queries щас только настоящие джедаи конструируют, все нормальные люди давно уже на статике сидят.
Re[4]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: LaPerouse  
Дата: 21.12.12 13:34
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, LaPerouse, Вы писали:


LP>>Нужно исходить из реалий — сегодняшний мейнстрим, это SQL и/или объектно-реляционные средства с процедурными алгоритмами обработки. Как здесь может помочь DSL?


D>Ну как я себе вижу типичную эволюцию проекта: если обнаруживаются повторяющиеся куски кода, они выносятся в повторно используемые функции. А если обнаруживаются повторяющиеся паттерны, реализованные разными людьми вразнобой, их реализации приводятся к общему виду, и тут возникает возможность тоже что-нибудь да подсократить, введя либо вспомогательное API, либо каркасное (в т.ч. макросы). А это уже можно рассматривать как DSL, даже если он не выглядит как "L" (откровенно говоря, помнить синтаксис настоящих "L" — так себе радость).


Если угодно называть API или SPI DSL-ем, тогда нужно признать, что DSL сегодня используется в каждом первом проекте. Другое дело, что все это бесконечно далеко от описанного в первом посте WolfHound-a.
Социализм — это власть трудящихся и централизованная плановая экономика.
Re[4]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: LaPerouse  
Дата: 21.12.12 13:37
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, LaPerouse, Вы писали:


LP>>Нужно исходить из реалий — сегодняшний мейнстрим, это SQL и/или объектно-реляционные средства с процедурными алгоритмами обработки. Как здесь может помочь DSL?


D>О, кстати! Про слона-то я и забыл! LINQ — это ж тоже DSL (по крайней мере, я так слышал, его к C# присобачили как захардкоженное синтаксическое расширение). А его аналоги на scala — и подавно DSL-и. Raw string queries щас только настоящие джедаи конструируют, все нормальные люди давно уже на статике сидят.


В яве подобное непопулярно. Использование орм носит массовый характер. А LINQ вроде как является частью языка общего назначения. Ну ладно, даже если назвать его DSL-ем, он применяется достаточно часто, чтобы сетовать на игнорирование современными программистами возможностей DSL...
Социализм — это власть трудящихся и централизованная плановая экономика.
Re[5]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 13:45
Оценка:
Здравствуйте, LaPerouse, Вы писали:

LP>Если угодно называть API или SPI DSL-ем, тогда нужно признать, что DSL сегодня используется в каждом первом проекте. Другое дело, что все это бесконечно далеко от описанного в первом посте WolfHound-a.


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

Вот посмотреть на те же scala DSL: можно было бы написать методы Process.setOutputStream(os) и Process.run(), а они написали методы Process.>>(os) и Process.!(). По сути — не поменялось ничего. Синтаксически — код, использующий это API, стал гораздо короче, выразительнее и зачастую непонятнее — ни читающему, ни пишущему.

Та ж фигня, к примеру, с коллекциями: вместо ListBuffer.append(T) у нас имеется ListBuffer.+=(T). Можно ли считать DSL-ем запись

buf += "hello " += "world!"


вместо

buf.append("hello ").append("world")?


Да хрен его знает, товарищ начальник, а почему нет? Разработчики и юзеры скалы — называют. ИМХО тут спор терминологический уже.
Re[5]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 13:54
Оценка:
Здравствуйте, LaPerouse, Вы писали:

LP>В яве подобное непопулярно. Использование орм носит массовый характер.


Не имея желания начинать очередной срач anemic vs rich, равно как и обсуждать говномамонтовость явы, на которой нормальная реализация LINQ-подобной фигни без препроцессора попросту невозможна (хотя Cyberax не так давно говорил, что что-то такое имеется, но кажется даже название не дал), скажу лишь, что тот же хиберовский HQL — тоже ж DSL, по сути. То, что он не статически типизированный и на этапе компиляции не проверяется — вопрос ортогональный.
Re: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: koodeer  
Дата: 21.12.12 14:02
Оценка: +1
Здравствуйте, LaPerouse.

Я думал-думал, я всё понял!

Главная причина непринятия DSL — крайне неудачные примеры введения в метапрограммирование. Сколько я видел кратких введений в МП — везде приводятся крайне неудачные примеры: ставится простенькая задача, пишется функция, решающая эту задачу, затем вместо функции пишется расширение (оператор) языка, решающий ту же задачу. Увы, именно такими примерами напрочь отбивают принятие МП и DSL. Ведь сокращение кода не происходит: была функция, стало новое слово языка; упрощение кода тоже не происходит (по той же причине).

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

Просто нужна правильная пропаганда. Нужны правильные примеры. Да, здесь, на РСДН, такие примеры есть (естессно, на Немерле). Здесь приведено множество крайне удачных примеров, когда МП в разы сокращает код, избавляет от копипасты, упрощает понимание кода. Увы, лишь некоторые дотнетчики обращают на них внимание. Все остальные — продолжают оставаться в дремучем неведении, что МП и DSL — не нужны.
Re[6]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: LaPerouse  
Дата: 21.12.12 14:14
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, LaPerouse, Вы писали:


LP>>Если угодно называть API или SPI DSL-ем, тогда нужно признать, что DSL сегодня используется в каждом первом проекте. Другое дело, что все это бесконечно далеко от описанного в первом посте WolfHound-a.


D>Перечитал тот пост, по ссылкам оттуда ходить лень, а без них не особо информативно. Но ничё, нам не впервой. :))


По ссылкам там реализация Datalog-a — компилятор, интерпретатор и рантайм. Причем разработка грамматики и парсера как таковой (то, что выдвигалось в качестве основной сложности разработки DSL) занимает от объема всей работы нуль целых хрен десятых. Основная сложность — разработка интерпретатора Datalog-a.

D>Да хрен его знает, товарищ начальник, а почему нет? Разработчики и юзеры скалы — называют. ИМХО тут спор терминологический уже.


Подобные вещи всегда называли сахаром. Но если называть отдельные структуры языка общего назначения дсл-ем, действительно, что же тогда dsl?
Социализм — это власть трудящихся и централизованная плановая экономика.
Re[2]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 14:14
Оценка:
Здравствуйте, koodeer, Вы писали:

K>Главная причина непринятия DSL — крайне неудачные примеры введения в метапрограммирование. Сколько я видел кратких введений в МП — везде приводятся крайне неудачные примеры: ставится простенькая задача, пишется функция, решающая эту задачу, затем вместо функции пишется расширение (оператор) языка, решающий ту же задачу. Увы, именно такими примерами напрочь отбивают принятие МП и DSL. Ведь сокращение кода не происходит: была функция, стало новое слово языка; упрощение кода тоже не происходит (по той же причине).


Я вот щас полуфлеймовый полуоффтоп забабахаю. Не надо это воспринимать как несогласие с написанным, просто к слову пришлось. Уж сколько ругани было вокруг неудачного примера вычисления факториала, на котором в школах показывают рекурсию! Сколько ругани!!! И в реале, и в интернете. Мол, нафига городить огород с рекурсией там, где можно обойтись простым циклом? Я, разумеется, всячески поддерживал эту точку зрения. Пока не начал ФП изучать... С тех пор каждый раз, вспоминая про этот многострадальный факториал, хожу-хихикаю.
Re[6]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: LaPerouse  
Дата: 21.12.12 14:18
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, LaPerouse, Вы писали:


LP>>В яве подобное непопулярно. Использование орм носит массовый характер.

D>тот же хиберовский HQL — тоже ж DSL, по сути. То, что он не статически типизированный и на этапе компиляции не проверяется — вопрос ортогональный.

Ну и SQL-то dsl не в меньшей степени.
Социализм — это власть трудящихся и централизованная плановая экономика.
Re[3]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: koodeer  
Дата: 21.12.12 14:24
Оценка: +1
Здравствуйте, dimgel, Вы писали:

D>Я вот щас полуфлеймовый полуоффтоп забабахаю. Не надо это воспринимать как несогласие с написанным, просто к слову пришлось. Уж сколько ругани было вокруг неудачного примера вычисления факториала, на котором в школах показывают рекурсию! Сколько ругани!!! И в реале, и в интернете. Мол, нафига городить огород с рекурсией там, где можно обойтись простым циклом? Я, разумеется, всячески поддерживал эту точку зрения. Пока не начал ФП изучать... С тех пор каждый раз, вспоминая про этот многострадальный факториал, хожу-хихикаю.


Кстати, да, хороший пример. Я тоже всегда раздражался, видя примеры вычисления факториала рекурсией.
Однако, и в ФЯП простая рекурсия не годится. Нужно её привести к хвостовой. Поэтому — хорошие примеры по-прежнему важны.
Re[7]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 14:26
Оценка:
Здравствуйте, LaPerouse, Вы писали:

D>>Да хрен его знает, товарищ начальник, а почему нет? Разработчики и юзеры скалы — называют. ИМХО тут спор терминологический уже.


LP>Подобные вещи всегда называли сахаром. Но если называть отдельные структуры языка общего назначения дсл-ем, действительно, что же тогда dsl?


Ну, может я неточно выразился. Называют DSL-ем они не такую тривиальщину типа ListBuffer, а вещи чуть посложнее. Но где эта грань проходит — хз. Возможность записи любых функций в инфиксной форме, плюс implicits, дают иной раз такие странные вещи, которые вообще непонятно как работают и как это чудо вообще рожали. Вот примерчик, как выглядит scalatest:

class ListBufferSpec extends FlatSpec with MustMatchers {
    it must "append items" in {
        val buf = new ListBuffer[Int]
        buf += 0
        buf.size must be(1)
    }
}


Я вот знаю, что кроме кучи вспомогательных классов-обёрток, кучи неявных преобразований типов, и кучи инфиксных вызовов методов здесь ничего быть не может. Но я хрен её знает, товарищ начальник, как эта хрень на самом деле компиляется и работает, и честно говоря, у меня начинает дёргаться глаз от одной мысли попытаться разобраться. Вот субъективно для меня это и есть, походу, отличие между скаловским сахаром и скаловскими DSL-ями: если понятно, как работает, — значит сахар, непонятно — значит DSL. Такая вот, панимаеш, петрушка. :/ Я очень жду, когда ж в скале появятся нормальные макросы, это должно радиально уменьшить чудесатость нынешних решений. (Походу, через год в версии 2.11 они появятся; первую попытку следует признать сырой и убогой.)
Re[7]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: dimgel Россия https://github.com/dimgel
Дата: 21.12.12 14:28
Оценка:
Здравствуйте, LaPerouse, Вы писали:

LP>Ну и SQL-то dsl не в меньшей степени.


То ж была такая ж мысль, пока писал. Но мысль была нечёткой, и я побоялся такие громкие заявы делать.
Re[2]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: koodeer  
Дата: 21.12.12 17:03
Оценка: +3 :))) :)
Я писал:

K>Просто нужна правильная пропаганда.


Ещё немножко о необходимости пропаганды.

Я зареган на RSDN относительно недавно, но посещал этот ресурс уже давно (в режиме ридонли). Так вот, я помню времена, когда при упоминании немерлистами слова "макрос" на них тут же накидывались: "макросы — зло!"
Время шло, немерлисты продолжали упоминать всуе свои макросы, и местная публика постепенно привыкла. Большинство, по моему мнению, по-прежнему не понимают, что именно представляют из себя гигиенические макросы, но уже смирились с их присутствием где-то там, в параллельном мире. И, даже не понимая их, многие осторожно признают их полезность
Теперь мы видим аналогичные нападки на любое упоминание DSL. WolfHound растревожил улей.
Нужно просто продолжать часто упоминать как макросы, так и DSL, причём всегда с понятными примерами, и серые массы программерства свыкнутся с этим, перестанут кидаться как на красную тряпку.
Re: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: minorlogic Украина  
Дата: 21.12.12 22:38
Оценка: :)
Создание программы на ЯОН не что иное как написание DSL. Вводить же для DSL другой синтаксис чем используемый в ЯОН было бы, мягко говоря, странно.

Отсюда ЯОН vs DSL существует только в воспаленном воображении.
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[7]: А при чем тут DSL? (в продолжении темы о языках общего назначения)
От: Aikin Беларусь kavaleu.ru
Дата: 22.12.12 06:48
Оценка: 12 (1) +2
Здравствуйте, LaPerouse, Вы писали:

D>>Да хрен его знает, товарищ начальник, а почему нет? Разработчики и юзеры скалы — называют. ИМХО тут спор терминологический уже.

LP>Подобные вещи всегда называли сахаром. Но если называть отдельные структуры языка общего назначения дсл-ем, действительно, что же тогда dsl?
Вы забываете про другую букву в абривиатуре DSL, а именно D. Есть домен -- можно создать ДСЛ. Нет домена -- будет синтаксический сахар, ИХМО.
Я со скалой практически не знаком, но, ИХМО, в том что показывал dimgel домена нет.
Если мы обратимся к linq, то там домен есть -- наборы данных. Поэтому Linq -- DSL.

Еще хороший пример ДСЛ внутри языка общего назначения -- библиотека numpy для питона. Dомен -- операции над матрицами, списками. L у него страшный, с непривычки. Но задачу он свою решает великолепно.
Вот отсюда
Автор: Manticore
Дата: 14.09.12
:
hsv_map = np.arange(2000 * 3000 * 3).reshape(2000, 3000, 3)

h = hsv_map[:,:,0]
s = hsv_map[:,:,1]
v = hsv_map[:,:,2]

c = (h < 15) | ((h > 165) & (h < 195))
a = 255 * (v >= 245)
b = 255 * ((v < 245) & (v > 90) & (s > 20) & ((s < 90) & c))

hsv_map[:,:,0] = a
hsv_map[:,:,1] = b

И цитата оттуда же:

У меня этот код выполняется за 0.6 секунд вместо 40 для оригинального кода. Если сделать тип hsv_map реалистичным (uint8, если я правильно понял значение букв h, s, v) вместо int64, каким он выставляется по дефолту, то время уменьшается до 0.34 сек. Можно ускорить еще больше с помощью scipy.weave, но это уже почти С

Ну а если кто хочет еще посчитать строчки или, что лучше, сравнить сложность, то сравнивайте с решением в лоб
Автор: catBasilio
Дата: 14.09.12


СУВ, Aikin
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.