Re[30]: на чём писать бэкенд?
От: Cyberax Марс  
Дата: 16.04.19 20:45
Оценка:
Здравствуйте, novitk, Вы писали:

N>·>А чем это принципиально отличается от нормальной IDE? А то я уж много лет использую Code Fragment Evaluation в Java, с подсветкой/автодополнением/навигацией/рефакторингами/етс и не думал, что это что-то невозможное.

N>Тем что не first-class. Функцию не создать, тип не создать, истории нет, сохранить значение на время некуда и т.д.
Функцию создать можно, прямо в исходнике. В Java есть поддержка замены байт-кода в отладчике. Тип не создать, да.
Sapienti sat!
Re[33]: Сполски
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 17.04.19 01:15
Оценка:
Здравствуйте, so5team, Вы писали:

S>* в Lisp-е каждый разработчик создает под себя собственный диалект. На коротком временном отрезке это проблемы не составляет. На длительном проблемы могут возникнуть при попытках переиспользовать фрагменты одной подсистемы в другой. Не говоря уже про подключение новых людей (пусть даже таких людей потребуется больше);


Ладно, берем отлично известный вам https://github.com/eao197/so-5-5 и смотрим в исходники с прицелом на то, во сколько обойдется поддержка такого фреймворка. Что же мы видим? Поддержка возможно либо небольшой очень высококлассной командой C++ разработчиков, либо компанией с бесконечными ресурсами которая может таковых нанять. Обычная компания, с обычными разработчиками даже людей новых подключить не сможет так, что бы все красоты не сломать. И чем это лучше LISP или Haskell? А ничем, та же невероятно высокая цена поддержки, тот же свой язык. Я наугад в файл ткнул и получил SO_5_CHECK_INVARIANT, который судя по всем заглавным, наверное, макрос (ага, привет, LISP).

S>* Lisp-ы, ЕМНИП, являются динамически-типизированными чуть больше, чем полностью. Соответственно, по мере роста системы, устаревания ее кодовой базы, расширять систему новым функционалом и изменять существующий функционал будет все сложнее и сложнее.


Да, вот эта проблема есть и она довольно остра для любого динамического языка. Но! А сильно ли ситуацию лучше с кодом на статически типизированным языке, плотно покрытым тестами? И вроде без тестов никак, а поддержка тоже больших усилий стоит. Ну и с динамикой тоже самое
Отредактировано 17.04.2019 2:06 kaa.python . Предыдущая версия .
Re[33]: Сполски
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 17.04.19 01:20
Оценка:
Здравствуйте, novitk, Вы писали:

N>BS. Если ты мне не веришь можешь посмотреть Norwig-a и других лисперов. Принципиальной разницы по скорости нет, но Питон лучше читается и экосистема намного обширней.

N>LISP прекрасен, но с Хаскеллем я бы не сравнивал, он все же намного проще в использовании.

В современном мире я не думаю что есть хоть какой-то смысл в использовании старых LISP-ов, есть же Clojure, которая благодаря JVM фактически нивелирует разницу в количестве доступных библиотек с Python
Re[34]: Сполски
От: so5team https://stiffstream.com
Дата: 17.04.19 05:31
Оценка:
Здравствуйте, kaa.python, Вы писали:

KP>И чем это лучше LISP или Haskell?


В теории -- ни чем. На практике, все-таки лучше. По двум причинам, как минимум. В C++ возможностей по DSL-естроению меньше, чем в Haskell и, тем более, в Lisp. C++ников на рынке труда, исторически так случилось, сильно больше, чем хаскеллистов и лисперов.

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

Выбор C++ в Yahoo для замены Lisp-у так же вызывает вопросы. И, есть подозрение, что C++ был выбран потому, что Yahoo решил переписать viaweb вскорее после покупки, еще в 1990-е годы. Когда та же Java была слишком молодой и тормозной. Процесс переписывания занял несколько лет и в 2003-ом году произошел полный и окончательный переход на новую версию.

Если бы решение о переписывании принималось в 2000-е, то Java могла бы быть более подходящим вариантом.

S>>* Lisp-ы, ЕМНИП, являются динамически-типизированными чуть больше, чем полностью. Соответственно, по мере роста системы, устаревания ее кодовой базы, расширять систему новым функционалом и изменять существующий функционал будет все сложнее и сложнее.


KP>Да, вот эта проблема есть и она довольно остра для любого динамического языка. Но! А сильно ли ситуацию лучше с кодом на статически типизированным языке, плотно покрытым тестами?


Сильно. И дело не в тестах. Дело в обозримости кода. Когда без плясок с бубном и REPL-а можно понять, что идет на вход, что на выход. В случае языков, вроде D, Ada и Eiffel явно выражены контракты и инварианты сущностей. В случае Java явно видны бросаемые исключения. В случае языков, вроде Go и Rust (а так же в функциональных языках) явно видно, что возвращается в нормальном случае, что при возникновении ошибки. В Haskell-е видны места с побочными эффектами.

Все это лежит на поверхности и даже не требует дополнительных инструментов. Что уж говорить о том, насколько упрощают работу с кодовой базой на статически типизированном языке инструменты вроде IDE и различных анализаторов.
Re: на чём писать бэкенд?
От: loginx  
Дата: 17.04.19 09:43
Оценка:
Здравствуйте, Dair, Вы писали:

D>D>Я в основном умею в C++, но пишу ещё на Swift и Kotlin.

D>Мне надо ВНЕЗАПНО написать бэкенд для мобильного приложения.

ну и как итог, все же что выбрано?
php + MySQL ?
Re[30]: на чём писать бэкенд?
От: · Великобритания  
Дата: 17.04.19 10:08
Оценка: +1
Здравствуйте, novitk, Вы писали:

N>·>А чем это принципиально отличается от нормальной IDE? А то я уж много лет использую Code Fragment Evaluation в Java, с подсветкой/автодополнением/навигацией/рефакторингами/етс и не думал, что это что-то невозможное.

N>Тем что не first-class. Функцию не создать, тип не создать, истории нет, сохранить значение на время некуда и т.д.
Согласен. Есть кое-какие ограничения. Но если быть до конца честным, то _тип_ в том же Питоне-лиспе не создать вообще, и в репле в том числе. А Map<> создавать можно и в java.
Функцию таки можно создать в каком-то смысле — лямбды же.
Новый тип создать таки можно, как новый класс и тут же скомпиилровать (горячая замена кода). Добавлять методы-поля тоже можно. Тела методов менять тоже можно. Структурно модифицировать нельзя (хотя вроде есть http://hotswapagent.org/ , но я не пользовался, мне хватает стандартных возможностей).
История есть в виде lru, но в git, понятное дело, не кладётся. В репле, как я понимаю, ровно то же.
Для сценария описанного тут
Автор: Sinclair
Дата: 16.04.19
хватает с головой. А изощрённый случай — подключиться отладчиком к работающему процессу на другом хосте, и выполнить там кусочек кода, агрегирующий и сбрасывающий некие данные из памяти в файл в неком виде — недоступен даже реплам.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[31]: на чём писать бэкенд?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.19 11:06
Оценка: +2
Здравствуйте, novitk, Вы писали:
N>Я в принципе согласен и не впадаю в крайности. Для долгоиграющего "глубокого" кода статика оправдана. Однако для быстрого результата и/или задачи с часто изменяемыми требованиями динамика имхо в настоящий момент подходит лучше. Возможно Scala 3 или что-то еще изменит это, но пока вот-так.
Я склоняюсь к некоему комбинированному подходу. То есть статика — однозначно лучше, кроме тех моментов, когда она путается под ногами.
В принципе, уже сейчас (на уровне vs2017) это "путание под ногами" сильно сведено к минимуму.
То есть я могу написать где угодно item.ProductCode, и в два клика сделать так, чтобы у нужного класса было задекларировано свойство ProductCode. Это в значительной мере снимает аргумент "да вы вспотеете все классы заранее описывать".
Можно писать ровно как в динамике, а потом сказать "да, вот это та модель, которую я сейчас вижу — пжалста, проверьте её на непротиворечивость".
Вторая сила динамики — возможность шарашить произвольное метапрограммирование — ведь типов нет, поэтому я могу в библиотеке реализовать произвольные договорённости.
Договорились, что вызов GetArbitraryNameByCode(104) будет делать select * from ArbitraryName where Code = 104 — и вперёд, на танки.

Не все такие договорённости одинаково полезны. Но современная статика опять-таки позволяет писать код с почти такой же эффективностью. А на худой конец есть Dynamic
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[32]: на чём писать бэкенд?
От: · Великобритания  
Дата: 17.04.19 11:17
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вторая сила динамики — возможность шарашить произвольное метапрограммирование — ведь типов нет, поэтому я могу в библиотеке реализовать произвольные договорённости.

S>Договорились, что вызов GetArbitraryNameByCode(104) будет делать select * from ArbitraryName where Code = 104 — и вперёд, на танки.
Так ведь статика особо такое не запрещает если очень хочется. Отличается чуток синтаксис, а по сути будет то же: Get("ArbitraryNameByCode").Call(104) — и вперёд, на танки. Притом сразу по коду явно видно, что впереди танки.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[33]: на чём писать бэкенд?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.19 13:50
Оценка:
Здравствуйте, ·, Вы писали:

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


S>>Вторая сила динамики — возможность шарашить произвольное метапрограммирование — ведь типов нет, поэтому я могу в библиотеке реализовать произвольные договорённости.

S>>Договорились, что вызов GetArbitraryNameByCode(104) будет делать select * from ArbitraryName where Code = 104 — и вперёд, на танки.
·>Так ведь статика особо такое не запрещает если очень хочется. Отличается чуток синтаксис, а по сути будет то же: Get("ArbitraryNameByCode").Call(104) — и вперёд, на танки. Притом сразу по коду явно видно, что впереди танки.
Не во всякой статике, скажем так. В наиболее адской форме динамики я вот этот вот GetXXX могу вызывать буквально на чём угодно; статика потребует отнаследоваться от типа, в котором есть описанный нами Get.
Отдельная песня начнётся в том случае, если мы захотим прозрачной перегрузки: я хочу написать библиотеку таким образом, чтобы пользователь мог, скажем, в конкретно своём объекте указывать руками, что GetPersonByName(name) будет делать select * from Person where (firstname like @name) or (lastname like @name) or (displayName like @name). И это всё будет прекрасно и бесшовно работать — а в (плохой) статике мне придётся прямо сильно озаботиться тем, чтобы это работало хотя бы вполовину также красиво.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[34]: Сполски
От: novitk США  
Дата: 17.04.19 13:51
Оценка:
Здравствуйте, kaa.python, Вы писали:

KP>В современном мире я не думаю что есть хоть какой-то смысл в использовании старых LISP-ов, есть же Clojure, которая благодаря JVM фактически нивелирует разницу в количестве доступных библиотек с Python


JVM не всегда оптимально: запуск медленный, JNI, инфраструктура громоздкая, балбесы в Oracle. Я знаю что для всего этого есть какие-то костыли, но они простоты не добавляют. Вон Amazon готов уже с убогим GоLang мучиться.

Что до clojure, то под явовские-библиотеки надо искать/писать обертки, если тебе не нужно пару функций, что не всегда тривиально. Смысл в clojure имхо, только если уже есть большие вложения в jvm. Грубо говоря или clojure/java или python/C++.
Re[32]: на чём писать бэкенд?
От: novitk США  
Дата: 17.04.19 14:44
Оценка: 2 (1) +1 -1
Здравствуйте, Sinclair, Вы писали:

S>Я склоняюсь к некоему комбинированному подходу. То есть статика — однозначно лучше, кроме тех моментов, когда она путается под ногами.


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

Например я начинаю в питоне с tuples/dict и (возможно!) уточняю типы в процессе до классов. Часто совершенно не понятен какой подход будет работать. Тут очень помогает что в динамики можно адаптировать минимальный кусок кода, а не весь проект. В VS ты будешь чинить все или должен внимательно следить где ошибки. Хочется попробовать свою кусок? Пиши тесты, а значит думай о правильной структуре проекта/билда, раздувай площадь кода. В динамике вместо этого в процессе используют репл, а тесты пишутся в самом конце, когда уже есть уверенность в результате.

Вы мне все тут рассказываете о вкусе устриц, как будто я их не ел. Для справки, я использую в настоящий момент на основном проекте одну из самых продвинутых IDE на одном из самых продвинутых статических языков. И вообще я бывший весь такой красноглазый плюсовик. Просто у меня после смены работы была возможность сравнить два подхода, так как очень похожие большие и сложные системы написаны на разных стеках(Питон с Скалой). При этом сам фреймворк по расчету рисков на Скале представляет собой полный "state-of-the-art". Представь себе что-то похожее на tensorflow, но строящие DAG-и из произвольного кода, а значит с полной статической проверкой типов и определителем non-referential transparent кода. И у меня даже, в отличие от C#/Java, есть repl и jupyter(https://zeppelin.apache.org/). И тем не менее стек имхо работает хуже Python: нужно больше железа, медленнее оборот в продакшен, дольше идет разработка, код сложнее и т.д.
Отредактировано 17.04.2019 14:47 novitk . Предыдущая версия . Еще …
Отредактировано 17.04.2019 14:46 novitk . Предыдущая версия .
Re[34]: на чём писать бэкенд?
От: · Великобритания  
Дата: 17.04.19 14:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>Вторая сила динамики — возможность шарашить произвольное метапрограммирование — ведь типов нет, поэтому я могу в библиотеке реализовать произвольные договорённости.

S>>>Договорились, что вызов GetArbitraryNameByCode(104) будет делать select * from ArbitraryName where Code = 104 — и вперёд, на танки.
S>·>Так ведь статика особо такое не запрещает если очень хочется. Отличается чуток синтаксис, а по сути будет то же: Get("ArbitraryNameByCode").Call(104) — и вперёд, на танки. Притом сразу по коду явно видно, что впереди танки.
S>Не во всякой статике, скажем так. В наиболее адской форме динамики я вот этот вот GetXXX могу вызывать буквально на чём угодно; статика потребует отнаследоваться от типа, в котором есть описанный нами Get.
Да ничего она особо не _требует_. Просто у нас тут свои приёмчики. Т.е. понятное дело, в лоб "как привыкли в динамике" сделать не получится, но другими способами можно получить практически то же. Например, вместо наследования использовать композицию, передавать зависимости через DI. Иметь какие-нибудь глобальные мапы "динамических типов" и т.п.

S>Отдельная песня начнётся в том случае, если мы захотим прозрачной перегрузки: я хочу написать библиотеку таким образом, чтобы пользователь мог, скажем, в конкретно своём объекте указывать руками, что GetPersonByName(name) будет делать select * from Person where (firstname like @name) or (lastname like @name) or (displayName like @name). И это всё будет прекрасно и бесшовно работать — а в (плохой) статике мне придётся прямо сильно озаботиться тем, чтобы это работало хотя бы вполовину также красиво.

Это всё решается красивым и продуманным дизайном. Например, данное требование можно выразить как-то так:
class SomeSpecificDevUsecase
{
    private final Magic magic = new MagicBuilder()
        .override("PersonByName", "select * from Person where (firstname like @name) or (lastname like @name) or (displayName like @name)")
        .createMagic();

    public void doSomething(String name)
    {
        var person = magic.Get("PersonByName").Call(name);
        var arbitraryName = magic.Get("ArbitraryNameByCode").Call(person.Get("code"));
    }
}

Но в целом согласен — там где в динамике ты начинаешь тупо кодить и оно [как бы] сразу работает, в статике приходится немного задумываться или искать красивую готовую либу. Хорошо это или плохо — зависит от.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[33]: на чём писать бэкенд?
От: koenig  
Дата: 17.04.19 15:50
Оценка: +1
N>Oна всегда путается под ногами. Дело не в механизации добавлении поля, а в том, что процесс создания кода совсем другой. В статике типы это главное, ты занят размышлениями о них с самого начала, а в динамике фокусируешься именно на задаче.

да как так-то
одинаково совершенно
просто в динамике типы не пишешь и на этом экономишь, пока ситуация позволяет
паникеры думают, что никогда не позволяет, потому что ковыряют копролиты переросшие допустимые для динамики размеры
Re[34]: на чём писать бэкенд?
От: novitk США  
Дата: 17.04.19 16:11
Оценка:
Здравствуйте, koenig, Вы писали:

K>да как так-то

K>одинаково совершенно
K>просто в динамике типы не пишешь и на этом экономишь, пока ситуация позволяет

Дело не в прописи типов, эта проблема в современной статике решена type inference. Я в Питоне стараюсь отложит создание своих классов вообще, фокусируясь лишь на функциях оперирующие над созданными до меня классами и встроенными гетерогенными коллекциями. Свои классы ввожу, когда код перестает хорошо выглядеть и становятся понятны случаи повторного использования.
Re[35]: Сполски
От: Cyberax Марс  
Дата: 17.04.19 17:06
Оценка: 1 (1)
Здравствуйте, novitk, Вы писали:

N>JVM не всегда оптимально: запуск медленный, JNI, инфраструктура громоздкая, балбесы в Oracle. Я знаю что для всего этого есть какие-то костыли, но они простоты не добавляют. Вон Amazon готов уже с убогим GоLang мучиться.

Амазон — это Java на 99%. Она там всех вполне устраивает.
Sapienti sat!
Re[36]: Сполски
От: novitk США  
Дата: 17.04.19 17:50
Оценка:
Здравствуйте, Cyberax, Вы писали:

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


N>>JVM не всегда оптимально: запуск медленный, JNI, инфраструктура громоздкая, балбесы в Oracle. Я знаю что для всего этого есть какие-то костыли, но они простоты не добавляют. Вон Amazon готов уже с убогим GоLang мучиться.

C>Амазон — это Java на 99%. Она там всех вполне устраивает.

Tы же там вроде и рассказываешь GoLang success stories?
Re[37]: Сполски
От: Cyberax Марс  
Дата: 17.04.19 18:43
Оценка: 1 (1)
Здравствуйте, novitk, Вы писали:

N>>>JVM не всегда оптимально: запуск медленный, JNI, инфраструктура громоздкая, балбесы в Oracle. Я знаю что для всего этого есть какие-то костыли, но они простоты не добавляют. Вон Amazon готов уже с убогим GоLang мучиться.

C>>Амазон — это Java на 99%. Она там всех вполне устраивает.
N>Tы же там вроде и рассказываешь GoLang success stories?
Я уже не там (совсем). Golang в Amazon'е есть, как и С++, Скала, Котлин, Rust и даже Perl. Но это всё смешные доли проектов.

В частности, Golang используется там, где его преимущества особенно видны. На нём написаны Amazon SSM и ECS Agent — системы, которые работают внутри пользовательских виртуалок.
Sapienti sat!
Re[35]: Сполски
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 17.04.19 23:30
Оценка:
Здравствуйте, novitk, Вы писали:

N>JVM не всегда оптимально: запуск медленный, JNI, инфраструктура громоздкая, балбесы в Oracle. Я знаю что для всего этого есть какие-то костыли, но они простоты не добавляют. Вон Amazon готов уже с убогим GоLang мучиться.


И правильно делают, Go позволяет совсем отсталых в разработке без малейшего вреда для проекта использовать. Тот же перевод разработки в Индию становится просто плевым делом.

N>Что до clojure, то под явовские-библиотеки надо искать/писать обертки, если тебе не нужно пару функций, что не всегда тривиально. Смысл в clojure имхо, только если уже есть большие вложения в jvm. Грубо говоря или clojure/java или python/C++.


Ничто не бесплатно, просто связка Clojure + Java сильно проще чем Python + C++ и как следствие позволяет получить результат быстрее. Если проект не числодробилка (отбрасываем движки игр и HFT), то связываться с C++ + Python вообще нет смысла, так как есть Go или JVM.
Re[36]: Сполски
От: Cyberax Марс  
Дата: 18.04.19 04:25
Оценка:
Здравствуйте, kaa.python, Вы писали:

N>>JVM не всегда оптимально: запуск медленный, JNI, инфраструктура громоздкая, балбесы в Oracle. Я знаю что для всего этого есть какие-то костыли, но они простоты не добавляют. Вон Amazon готов уже с убогим GоLang мучиться.

KP>И правильно делают, Go позволяет совсем отсталых в разработке без малейшего вреда для проекта использовать. Тот же перевод разработки в Индию становится просто плевым делом.
Это не так, для любого языка. Индусы (любой национальности) могут испортить что угодно.

И даже экзотические языки не являются стопроцентным фильтром. Я лично видел полных индусов, пишущих на Скале.
Sapienti sat!
Re[35]: на чём писать бэкенд?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.04.19 05:02
Оценка: +2
Здравствуйте, ·, Вы писали:
·>Да ничего она особо не _требует_. Просто у нас тут свои приёмчики. Т.е. понятное дело, в лоб "как привыкли в динамике" сделать не получится, но другими способами можно получить практически то же. Например, вместо наследования использовать композицию, передавать зависимости через DI. Иметь какие-нибудь глобальные мапы "динамических типов" и т.п.
Не, так не честно. Понятно, что можно обойти любую статику, просто сделав один класс Variant, и обращаться к нему как v.Exec("SetProperty", "PropertyA", 122).
Получится просто динамика с карательным синтаксисом.

S>>Отдельная песня начнётся в том случае, если мы захотим прозрачной перегрузки: я хочу написать библиотеку таким образом, чтобы пользователь мог, скажем, в конкретно своём объекте указывать руками, что GetPersonByName(name) будет делать select * from Person where (firstname like @name) or (lastname like @name) or (displayName like @name). И это всё будет прекрасно и бесшовно работать — а в (плохой) статике мне придётся прямо сильно озаботиться тем, чтобы это работало хотя бы вполовину также красиво.

·>Это всё решается красивым и продуманным дизайном. Например, данное требование можно выразить как-то так:
·>
·>class SomeSpecificDevUsecase
·>{
·>    private final Magic magic = new MagicBuilder()
·>        .override("PersonByName", "select * from Person where (firstname like @name) or (lastname like @name) or (displayName like @name)")
·>        .createMagic();

·>    public void doSomething(String name)
·>    {
·>        var person = magic.Get("PersonByName").Call(name);
·>        var arbitraryName = magic.Get("ArbitraryNameByCode").Call(person.Get("code"));
·>    }
·>}
·>

Ага. Осталось продумать детяли размещения кода. Одно дело, когда ты в проекте с 2000 типами сущностей в одной из них оверрайдишь один метод, а другое — когда тебе надо внести это в единую таблицу кастомизаций.
Динамика тем и хороша, что позволяет откладывать такие решения. В статике ты будешь ехать вот по этим рельсам, проложенным на заре проекта; перепилить это всё на другую архитектуру будет стоить столько, что проще будет на питон переписать.
Ну, либо опять весь прикладной код написан в максимально нечитаемом виде. Сравните свой код с этим:
    public void doSomething(String name)
    {
        var person = db.GetPersonByName(name);
        var arbitraryName = db.GetArbitraryNameByCode(person.code);
    }


·>Но в целом согласен — там где в динамике ты начинаешь тупо кодить и оно [как бы] сразу работает, в статике приходится немного задумываться или искать красивую готовую либу. Хорошо это или плохо — зависит от.

Я пытался объяснить, что на динамике можно ещё и писать красивые либы, не дожидаясь, пока авторы языка дадут тебе exention methods, или default implementation, или ещё что-то.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.