Джозеф Лесли Армстронг → Цитаты из выступлений

ПрограммированиеErlang/OTPElixir/PhoenixБиографии гиков
Recovery mode
Автор оригинала: Joe Armstrong
От переводчика: Джо Армстронг внес величайший вклад в становление Computer Science. Ниже предлагается перевод статьи из вики-цитатника, посвященной Джо.

Подзабытые преимущества параллельного программирования


Подзабытые преимущества параллельного программирования. Полная транскрипция также доступна на веб-сайте Erlang Solution. Записано в 2019 году.

  • Я хотел делать отказоустойчивые системы, и довольно скоро я понял, что невозможно построить отказоустойчивую систему на изолированном компьютере, потому что в любом месте компьютера может произойти сбой, поэтому мне нужно было много независимых компьютеров.
  • Доставка сообщений требует времени, и они распространяются через пространство. Нет никакой гарантии, что в конце концов сообщение будет доставлено адресату.
  • Я просто хочу смоделировать то, что происходит в реальном мире.
  • Нам нужно было построить мир, в котором есть параллельные процессы, общающиеся посредством передачи сообщений, и я думал, что они не могут иметь общую память, потому что если у них общая память, один сбой повлечет за собой остальные, как в эффекте домино.
  • Почему люди обмениваются только данными, а не программами? Мы могли бы делегировать и то, и другое — в некоторую промежуточную точку в середине, чтобы выполнить вычисления там.
  • Что действительно пугает меня, так это то, что люди разрабатывают большие приложения, про которые они не до конца понимают, как те вообще работают.
  • Многие существующие программы не имеют четко определенного интерфейса. Так быть не должно.
  • Я думаю, что мы, кажется, забыли, что вещи могут быть маленькими. Необходимо уметь разложить сложную систему на мелкие составные части, о которых можно рассуждать.
  • Люди продолжают говорить мне: «вы должны обновить свою операционную систему». Потом они добавляют: «Ну, это ради безопасности».- Я не очень-то им доверяю. Если бы они говорили, что мы должны обновляться раз в 20 лет, — я мог бы поверить, что это необходимо ради надежности, но говорить мне, что я должен что-то менять раз в полтора месяца — это безумие.
  • Одна из вещей, которую мы забыли, — это насколько важно иметь хорошие протоколы с исчерпывающими описаниями.
  • Если вы представите себе суперкомпьютер с петабайтами памяти, LiFi, и связью на десятках гигабит в секунду, типа комбинации десяти тысяч машин Cray, и все это малюсенькое, размером с ноготь, — везде, в каждой лампочке, — это как атомная бомба, поражающая программное обеспечение. Что мы будем с ним делать, никто не знает.
  • Развертывание — это всегда проблема, потому что даже если кто-то сделал приложение конфиденциальности с открытым исходным кодом, ему нужно 50 миллионов пользователей, чтобы взлететь. Apple, Google, да и все остальные, — доминируют именно потому, что могут раскатить что угодно для сотен миллионов людей. Кто первый получит сто миллионов пользователей — тот и выигрывает, как правило.
  • Google знает о нас все, но мы ничего не знаем о компании Google.
  • Я всегда представляю себе историка, который через два года, или триста лет, напишет историю этого периода. Это было похоже на темные века, на века смятения. Закончится ли это компьютерными сбоями, которые убивают миллионы людей, или это перейдет в нечто, что работает на благо человечества? Сейчас мы этого не знаем, и я не знаю, сколько времени пройдет, прежде чем мы это узнаем.

Забытые идеи в области информатики


Ошибки, забытые идеи в информатике, 16 марта 2018 года

  • У меня был план. Изучить Emacs. Изучить UNIX. И выучить язык программирования. Что случилось? Я не изучал Emacs. Я не изучил UNIX. И я изобрел новый язык программирования.
  • Стали ли мы лучше в программировании за последние 25 лет? Я думаю, что ответ будет нет. Это были небольшие улучшения.
  • Я начал программировать примерно в 76 году. Выбор языков был небогат. Там были Фортран и Кобол… И Фортран. Теперь есть 250, 500, 800 языков программирования, каждую неделю выходит новый.
  • Действительно ли нам нужно перехватывать внимание каждые 10 секунд с помощью баннера?
  • Методология для того, что я собирался сделать: задать несколько вопросов, получить некоторые ответы, упорядочить результат, выбрать лучшее, сделать.
  • Как составить список? Собрать данные, это просто. Сортировка по категориям немного сложнее. Сокращение списка — вот самая сложная часть. Это действительно сложная проблема.
  • Если кто-то составляет большой список, мой вопрос всегда: что самое важное в этом списке.
  • Я могу реализовать только одну вещь за раз. Я по натуре всего лишь человек, делающий одну вещь в каждый момент времени. Так что, если вы дадите мне этот огромный список, я буду реализовывать самое главное. И что же это такое?
  • Мы не можем сразу сделать все из длинного списка. Никто не в состоянии делать 20 вещей одновременно. Сделайте что-то одно. Потом переходите к следующему.
  • Первый вопрос всегда должен быть таким: какова самая трудная часть проблемы, которую я пытаюсь решить? Затем нужно попытаться решить именно эту часть. Если вы не можете решить самую сложную часть — сдавайтесь! Ищите совета, находите того, кто может решить эту проблему!
  • Полным провалом всего проекта закончится сознательный подход сначала к самым легким частям. Поскольку вы не сможете решить самую трудную часть, весь проект потерпит неудачу позже, что гораздо хуже, чем неудача на раннем этапе, и компания потеряет много денег.
  • Четыре хороших инструмента для изучения: Emacs, Bash, Make и Shell. Вы могли бы использовать Vi, я здесь не религиозен. Изучите их до мельчайших подробностей! Я использую Make для всего, и это очень круто.
  • Лучшие алгоритмы, вероятно, дали нам коэффициент производительности 6. Смена языка программирования с пролога на C, дало нам коэффициент 15. Все остальное приходит из аппаратных средств. Итак, подождите 10 лет, у вас есть тысяча. подождите 20, вы получите миллион в производительности оборудования.
  • Если вы пытаетесь возиться с вашим кодом, чтобы сделать его эффективным, это неправильно. Продолжайте следить за новейшим оборудованием. Это правильный подход.
  • Четыре языка для изучения: C, Prolog, Erlang, Javascript.
  • Позвольте мне просто сказать кое-что о перенаправлении стандартного ввода-вывода (pipes). Великая философия UNIX такова: вывод моей программы должен быть вводом в вашу программу, чтобы все заработало как единое целое.
  • А теперь пайпы убиты GUI. Таким образом, у вас нет потока текста в графический интерфейс. Итак, в царстве GUI, мы не можем соединять разные приложения вместе в последовательности. Это убило понятие пайпа — и понятие повторного использования вещей.
  • Мы можем использовать машинное обучение для диагностики заболеваний, но нам не стоило использовать машинное обучение для таргетинга рекламы на людей. Мы можем использовать его для лечения рака. Просто подумайте об этом.
  • Я был хреновым писателем, но я все-таки научился не вываливаться из седла. Я написал несколько книг, а после того, как ты написал несколько книг, у тебя это получается уже неплохо. Но в самом начале я действительно был никчемным писателем.
  • PHP — блестящий язык! Ну, помимо синтаксиса и семантики. Потому что все, что будет выполнено сосредоточено в одном месте.
  • Как помочь вашему неразбирающемуся в технике соседу. Скажите ему, что это не ваша вина. Скажите им, что это дерьмовое программное обеспечение, и что вы сами не вполне разбираетесь во всем этом.
  • Вещи могут быть маленькими. Они не обязательно должны измеряться гигабайтами.
  • Интернет полностью сломан. Он не симметричен. Легко читать вещи, очень трудно писать вещи. У нас есть сообщество пользователей, которые пассивно занимаются чтением материала, но сами они ничего не пишут.
  • Давайте-ка починим интернет. Интернет сломан. Давайте соберемся и починим его, и давайте вернем возможность производить вычисления этим суперкомпьютерам, которые у нас есть в карманах […] Давайте обеспечим, что наши личные данные принадлежат нам, а не крупными корпорациями […] Давайте снова упростим вычисления, как это было в прошлом. Давайте создадим приложения, которые могли бы взаимодействовать друг с другом.

Джо Армстронг берет интервью у Алана Кея


Джо Армстронг интервьюирует Алана Кея, Code Mesh Лондон, Ноябрь 3-4, 2016

  • Я использовал Smalltalk, и я никогда на самом деле не мог перейти эту границу своего рода моделирования структуры класса в голове, чтобы я мог потом ее запрограммировать. Это было больно, я вынужден был вчитываться в список изменений, чтобы понять, что, да почему. Я сожалею, что вы решили сделать именно так.
  • Я смотрел на Prolog, и я думал, хорошо бы добавить сообщения, передающие объектную модель. Таково было влияние CSP.
  • Если вы делаете проект, которому гарантирован успех, подумайте, что, скорее всего, он получится ужасно консервативным. Нельзя просто так делать то и это. Сотни других проектов могли бы быть гораздо веселее, но все они потерпят неудачу!
  • Каждый процесс Erlang во всей вселенной должен быть адресуемым, и поддерживать трассировку, — если мы хотим, чтобы они могли общаться друг с другом.
  • Одна из вещей, которая меня беспокоит, — это глупая идея с облаком. Если мы просто сохраняем все в облаке, непреднамеренный побочный эффект этого заключается в том, что мы, вероятно, потеряем всю нашу историю. И я вроде как волнуюсь за свою жизнь. Пройдет двести лет, и, оглянувшись назад, мы вынуждены будем констатировать: пардон, но мы просто потеряли всю историю последних двадцати веков.

Ошибки, масштабирование и параллелизм Эрланга


Ошибки, масштабирование и параллелизм Эрланга, 24 сентября 2014 года

  • Для того, чтобы справиться с отказом, вам нужно минимум две машины. […] Вы должны быть заинтересованы в распределенном программировании. Вы должны быть заинтересованы в параллельном программировании.
  • С одной стороны, я думаю, что Эрланг был своего рода программным обеспечением, эмулирующим тандемную машину.
  • Если вам удалось масштабировать горизонтально, вам удалось масштабировать вообще.
  • Вы можете сохранить как отказоустойчивость, так и масштабируемость. Вы можете иметь и то, и другое, или ни того, ни другого.
  • Общая память — зло.
  • Защитное программирование на языке Си необходимо только потому, что у вас есть только один поток вычислений. Если у вас есть язык с последовательной обработкой, и он падает, — все потеряно.
  • В Эрланге у вас есть столько процессов, сколько вы хотите. Вы можете организовать процессы так, чтобы они наблюдали друг за другом. Если у вас есть полмиллиона процессов, чтобы что-то сделать, то какая разница, если несколько тысяч из них потерпят неудачу?
  • Краеугольным камнями отказоустойчивого программирования является необходимость изолировать ошибки, чтобы убедиться, что если что-нибудь поломалось, оно не потянет за собой все остальное. Это то, что делают процессы в операционной системе.
  • Плохая модель параллелизма, я думаю, — вот что делает программирование искусственно сложным.
  • Если вы не можете обнаружить сбои, вы не можете восстановиться от них. Это очевидный принцип, понимание которого необходимо, как воздух.
  • Нам нужно знать, почему оно упало. Если мы хотим улучшить нашу систему, нам нужно поместить эту информацию в эту систему: в журналы или куда-то там еще.
  • Процессы архитектурно изолированы. Переключение контекста очень легкие. Эти процессы по самой своей конструкции не могут навредить друг другу.
  • Наша цель состоит в том, чтобы на самом деле иметь около 75% использования, если бы у нас было сто процессоров, мы бы надеялись, что наша программа будет работать в 75 раз быстрее, никак специально ее не настраивая.

Моделирование мира с помощью процессов объектов функций отношений


26 лет вместе с Эрлангом, 22 сентября 2014 года

  • Композиция вычислений. Почему мы вообще объединяем вычисления? Потому что мы хотим сделать многоразово переиспользуемые вещи из маленьких кирпичиков. Мы берем маленькие штучки, чтобы сделать большие штуковины.
  • В Haskell, C или Java, или любом из этих языков, неправильная композиция функций поломается во время компиляции. Система типов скажет: мы не позволим вам соединить эти штуки вместе, потому что эти типы не совпадают. Но в Erlang вы можете соединить все, что вам чертовски хотелось бы просто видеть объединенным вместе, и знать это во время выполнения, а не во время компиляции. Некоторые люди думают, что это не так уж и хорошо. Некоторые люди думают, что это довольно круто. Я скажу вам, почему это довольно круто. На самом деле это поздняя привязка, и решение о связывании принимается максимально поздно.
  • В любом языке программирования, скрытое состояние объекта означает, что вы не можете просто создавать разные вещи. Скрытое состояние — это просто эффект состояния, который мешает вам создавать то, что вам хочется.
  • В чисто функциональных языках, вам не позволено возиться с состоянием, поэтому состояние передается в вашу функцию, дальше что-то происходит, и состояние, возможно обновленное, возвращается обратно. В чистом функциональном языке вы получили состояние на вход, а на выходе — какой-то результат, и новое состояние.
  • Объектно-ориентированное программирование, это искусство сокрытия побочных эффектов. Если вы умеете делать это хорошо — вы можете с успехом писать программы. Если вы делаете это неудачно, вы рано или поздно обнаружите себя в диком хаосе.
  • Различные способы сокрытия состояния — это то, что предлагают различные функциональные языки программирования. В Хаскелле это называют монадами, в Эрланге — процессами.
  • Pipes — это самое главное слово. Итак, монада — это pipe. Это позволяет вам передать выходной сигнал функции на вход другой функции, и так сколько угодно раз. Они переизобрели unix pipes.
  • Мы могли бы, конечно, переопределить композицию так, чтобы она работала и с отладочной информацией тоже. Это было бы ужасно. Это означало бы, что нам нужно было бы создать кучу чистых функций. Нам нужно было бы создать функции для отладки и для всего остального на планете. Поэтому вместо разных версий compose, проддерживающих все на свете, мы не трогаем compose, а вместо этого напишем небольшую функцию, которая принимает функцию, которая не умеет в отладку, и возвращает функцию, которая выполняет отладку. Пусть у нас есть функция, которая принимает только один аргумент, версия без отладочной информации, — мы добавим еще одну маленькую функцию, которая автоматически добавляет к ней аргумент. Когда мы это сделаем, мы можем привязать вызов compose, и все будет работать, и всем станет хорошо. И будет мир на земле, и все будут радоваться и веселиться.
  • Что поистине хорошо с пайплайнами, так жто то, что все довольно прозрачно, и довольно легко увидеть что и как, и все шаги в пайплайне можно выполнять параллельно.
  • В Эрланге F(G(X)) используется для небольших шагов. Пайплайны используются для больших шагов. Семантика больших шагов — это процессы. Это сообщения между процессами. Процессы используются для больших вычислений. Когда вы получили F(G(X)) — это маленький шаг, и мы сопоставляем небольшие шаги на том же ядре. Мы планируем большие шаги по процессам. Отображение процессов на многоядерных системах. Система просто делает это за вас.
  • Если понятия не имеете, что и где у вас в доме протекает, вы обычно звоните профессиональному сантехнику, у которого будет оборудование, чтобы найти трубы в ваших стенах […] И да, с программным обеспечением все ровно точно так же!
  • Посредник — вот ключевая абстракция в Эрланге. Это и есть модель мира. Все в мире — это процессы. Когда когда-либо у вас была какая-либо проблема, не важно даже, что именно за проблема, вы ставите датчик в самую середину. Он траслирует вам то, как разные части общаются друг с другом, внутри себя, и с внешним миром.
  • Посредник — это не только то, что придумано в мире Эрланга. Если люди пытаются говорить друг с другом, сложность общения составляет O(N²). Но, если мы позовем посредника, это сразу превратит сложность в O(N).

26 лет с Эрлангом


26 лет с Эрлангом, июль 13, 2013

  • Я думаю, что первая компания, которая выберет стопроцентный Эрланг будет стоить миллиард долларов.
  • Я программировал на прологе […] Что происходит, когда в программа на прологе что-то пошло не так? Она говорит: «Нет». Но «нет» — не слишком-то хорошее сообщение об ошибке. Полная причина возникновения ошибки, и дамп — гораздо информативнее, чем «Нет».
  • Одна вещь, которую я хотел сделать с прологом — улучшить некоторые значимые вещи. Что-то получше должно происходить в случае сбоя, не просто «Нет». Другая проблема с прологом заключается в том, что вы можете сделать только одну вещь, и у вас может быть только один процесс. Несмотря на это, я действительно считаю, что пролог очень крутой. Я создал алгебру, чтобы описать телефонию в прологе, но единственная проблема заключалась в том, что все происходило в рамках одного процесса, и если он падал, все шло коту под хвост.
  • Спецификация не говорит мне, что делать. Что я должен запрограммировать? […] Я думал, что это безумие, мы создаем продукты со спецификациями и прочими документациями, и спецификации не говорят, что должно произойти. Затем, когда вы доберетесь до этой точки в коде, где вы не знаете, что произойдет, — все понимают, как это окажется запрограммировано. Я думал, что это безумие, потому что все могут интерпретировать это по-разному, и каждый может принять какое-то свое решение. Поэтому я подумал про себя, что единственный разумный поступок — это сбой. Мы просто разрываем этот звонок, потому что не знаем, что делать.
  • В файловой системе, если программа открывает файл и делает что-то, а затем аварийно завершает работу, операционная система закрывает файл для вас. Он никем не используется, и поэтому вы можете использовать его снова. Поэтому я подумал про себя: ну, звонок — это как файл. Итак, что произойдет, если вы выполните этот трюк, аварийно завершите работу программы. Кто-то другой (операционная система) все равно присматривает за всем, и она закроет звонок точно так же, как закрывает файл. Таково было происхождение этой штуки, которую я теперь называю Let It Crash.
  • Построить прототипы системы телефонии было на самом деле не долго, но они работали очень медленно. На том этапе мы сказали: «Давайте сделаем из этого продукт». Мы думали, что семантика была в порядке, но реализация явно хромала.
  • Я думаю, что такие вещи, как PowerPoint, в некотором роде разрушили творчество.
  • Я никогда не буду делать никаких заявлений о производительности, которая не основана на измерении, и я никогда никому не буду советовать, как писать код, помимо совета «написать и измерить производительность». Я не сторонник спекуляций. Я думаю, что люди, которые действительно размышляют о производительности, должны немедленно перестать размышлять, написать код, запустить его и измерить производительность.
  • Я худший в мире программист C, потому что это так сложно, что я не могу понять как это делать правильно […] Все, что я знаю, это то, что всякий раз, когда я пишу на C, мои указатели всегда смотрят куда-то не туда.
  • Вы программист, вы только что написали 100 строк кода, которые программисты на C++ не осилили. Вы, конечно, хотите показать его людям, потому что вы очень довольны и горды. И вы думаете, людям это понравится…

Более века программирования


Более века программирования, июнь 2013 года

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

Вопрос о том, как и почему все это совместить


О том, как и почему все это совместить, Erlang User Conference 2013

  • Почему проектировщики языков программирования так уперты в требовании правильности? Это потому, что, если вы пишете программу на Эрланге, и она внезапно не работает, все обвинят меня. Но если она заработает, все похвалят вас.
  • Эрланг похож на конструктор Meccano. Это очень хорошо. Можно создать удивительные вещи с Meccano. C++ — как Lego. Лего — это тоже замечательно. Но нужен какой-нибудь клей, способ соединить части разных конструкторов вместе. Если вы просто возьмете абы какой клей, чтобы склеить части Lego и Meccano, ничего не выйдет.
  • Это фантастика, это квантовый скачок в абстракции. Большинство людей смотрят в черные ящики и пытаются понять, как они работают, но я думаю, что они смотрят не туда. Они должны были заглянуть внутрь черного ящика, чтобы понять, как работает программирование, и посмотреть на связанные вещи, чтобы понять, что они делают.
  • Все интересно, все со всем связано, но ничего не работает.
  • почему мы все время пишем вещи с нуля и переосмысливаем их? Потому что это быстрее, чем найти другой язык программирования, который делает это. Мы не можем найти эту программу, и если мы находим ее, она делает что-то немного отличающееся от того, что мы хотели, и быстрее написать новую программу с нуля, чем изменить старую программу.
  • Закон Джо: фреймворки усложняются до тех пор, пока уже никто не способен их использовать.
  • Сообщения — как файлы. Нам все равно, как эта сущность была создана.
  • Pipes — это круто. Они делают замечательные вещи.
  • Плохие идеи в области компьютерных наук, как и везде, весьма прилипчивы. Если пришедшая первой идея была неудачной, но она сработала, люди будут повторять ее бесконечно.
  • Посредник (middle man, middleware) привносит в систему концептуальную целостность.

Несколько улучшений в Erlang


Несколько улучшений в Erlang, Erlang User Conference 2012

  • Я не мог понять, как люди вообще могли начать работать с PHP, потому что там вообще нет обмена сообщениями.
  • Я думал, что если вы умножите два целых числа вместе, вы получите целое число. Но, внезапно, в PHP это не так. Вы получаете число с плавающей запятой, которое становится все больше и больше, и это нормально. Пока вы не доберетесь до factorial(171), после чего вы получите "INF". Я не знаю, что такое "INF". Я думаю, что это, ну, бесконечность. Но в этом случае это, вероятно, не совсем так, потому что бесконечность ужасно велика. Явно больше, чем factorial(171)`.
  • Так что же не так с шеллом? И что не так с Эрлангом? — А они оба не LISP!
  • Метапрограммирование в Эрланге не очень-то легко из-за того, как анализируются деревья.
  • Мне не нравятся модули. Это своего рода баланс любви и ненависти. Проблема с модулями — это проблема классификации. Когда вы написали функцию, вы не всегда знаете, в какой же модули ее следует поместить.
  • Модули меняются со временем, а понятие «времени» отсутствует в процессе разработке программы. Если вы посмотрите на что-то вроде git, то способ, которым вы имеете дело со временем, — это контрольная сумма, отметка времени…
  • Когда мы пишем код, мы забываем о более широкой картине.
  • Наконец, после нескольких часов беспорядочного поиска в интернете, я могу написать свою программу. Вот это я и называю исследованием. Исследование занимает 3 часа, написание программы занимает 2 минуты, для окончательной версии. Вы публикуете программу, и вы выбрасываете исследование. Это не очень умно. Кто-то позже не может понять, как вы это сделали, потому что все, что он видит — это конечный результат.
  • Хорошая вещь в документации Эрланга — это то, что все сделано в XML. XML — абсолютно прекрасен для разметки текста.
  • Документы полны абзацев. Но у этих абзацев нет названия. Итак, мы не можем предметно адресовать и говорить об абзацах.

Программирование Erlang-программное обеспечение для параллельного мира


Программирование Erlang, программное обеспечение для параллельного мира, прагматичные программисты, 2007 год

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

Создание надежных распределенных систем при наличии программных ошибок


Создание надежных распределенных систем при наличии программных ошибок, декабрь 2003 г.

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

Фильм «Эрланг»


Фильм «Эрланг», 1990 год

  • Ясность языков программирования имеет ряд преимуществ перед традиционными языками. Например, программы на таких языках значительно короче, чем эквивалентные программы на императивных языках. Они также поощряют четкость в изложении мыслей.
  • До сих пор декларативные языки программирования, такие как Prolog или ML, не использовались для приложений реального времени.
  • Поэтому нам пришлось добавить в наш язык понятие параллелизма в реальном времени. Мы также добавили сложный механизм обнаружения ошибок. Это позволяет нам программировать надежные системы реального времени.
Теги:цитатыэрлангджо армстронг
Хабы: Программирование Erlang/OTP Elixir/Phoenix Биографии гиков
+8
1,3k 14
Комментировать

Похожие публикации

Лучшие публикации за сутки