Заявленные плюсы звучат очень хорошо, и если сама парадигма написания кода способствует этому - это супер круто. Единственное, что меня не порадовало, это итоговое содержание функции map. Серьёзно, тут же можно полдня только разбирать, что написано в этой строке xD Возможно я неправ, говорю строго на правах мнения, но дело в том, что в данном коде присутствует 4 уровня вложенности вызовов, но кроме того цепочка вызовов ещё и зацикливается: мы вызываем map, чтобы получить функцию, в которой вызывается map, чтобы получить функцию, в которой вызывается map, чтобы получить функцию... Может это дело привычки, и в итоге после некоторой практики такое перестаёт вызывать трудности при чтении, но когда видишь такой код впервые, то не можешь отделаться от мысли, что он ужасен :) Чувство приблизительно то же, что и знакомстве с регулярками, которые изначально воспринимаются не иначе как непонятный набор символов, делающий какую-то магию.
Ну есть в ФП ещё "частичное применение функции" и "каррирование" которые помогают вызвать функции более привычно, но в то же время использовать преимущества сокращения арности функции Например sum(1,3) // 4 sum(1) // a => a + 1 То есть можно написать inc = sum(1) - и передавать ее в конвееры, в всякие Монады.
@@21POPOV map = (arr) => (f) => arr.length === 0 ? [] : merge([f(head(arr))])(map(tail(arr))(f)); Сейчас только подумал, а почему функция не написана так. Я какой-то принцип нарушил, или в видео не оптимально написано (больше вложенность, чем надо)?
В начале видео есть полторы минуты, когда вы просто говорите, можно было, чтобы размытие плавно исчезало перед началом объяснения и плавно возвращалось после, кажется, было бы чуть лучше) А так, спасибо за видео)
ФП - для предсказуемости поведения программы, детерминированности выполнения, лёгкости тестирования. Так же помогает корректно обновлять состояние, особенно в многопоточной среде.
А не приводит ли это к неоправданому выделению памяти? В каждом вызове рекурсивной функции мы в замыкание ложим новый результат tail() и новый результат marge(), и все они живут пока не розвернется весь стек функции.
в третьем примере в реализации pipe надо было sizeOf тоже использовать вместо .length чтобы совсем уж по красоте. Но в целом вот этот вот висящий аккумулятор у map немного расстраивает. Если там сделать параметр по умолчанию это нормально будет или против парадигмы? или может переобозвать этот map в некий служебный accMap и его обернуть функцией map где уже не надо будет пихать пустой массив т.к. она будет сама вызывать accMap именно с пустым массивом ? тогда и второй пример бы уже выглядел получше. А так немного приходилось ставить на паузу и до пояснений тратить немного времени чтобы понять что да работает, да правильно и как именно. Такие головоломки сами по себе прикольная штука, это было интересно. Ещё видел на жабаскрипте похожую очень по стилю реализацию прикручивания кеширования (мемоизации если угодно) к произвольной функции, тоже хороший пример. Понятное дело что немного практики и к этому всему можно привыкнуть, но наверное лучше это изучать на каком-нибудь хаскеле где это будет естественно а потом уже привносить в js (на codewars кстати есть какое-то количество задач именно по такому принципу - давайте сделаем на js вот такую штуку по примеру хаскеля, достаточно сложные штуки там есть)
Функциональной парадигме не противоречит назвать эту штуку иначе (например flatMapConcat), а map описать как: cons map = f => xs => flatMapConcat(xs)(x => [f(x)])([]) Просто в видео не map. В другой ветке комментариев уже выяснили, что это flatMap с последующим concat :D
Было свободное время, взялся в качестве упражнения для ума разобраться с функционалкой. Автору честь и хвала, такой детальной проработки вопроса я не видел ни у кого (а большенство в лучшем случае останавливалось на чистых функциях). Только если ради каждого забитого в стену гвоздя должны кипеть такие страсти (а уровень абстракций реально зашкаливает)... "Назови мне хоть одну причину"(с) почему это все реально существует, и это двигают? Заговор мирового правительства? (желательно с той же степенью детализации и убедительности). С таким счетом на табло при сравнении кода для решения задачи, команду, за которую играл автор, пора разгонять
04:30 Так хочется сделать Array.prototype.map = null :))) P.S. Про читаемость кода, я понял, что lst это list, а не last, только после того, как было озвучено, что "всё есть список". Концепция, на вид, крутая - обязательно попробую.
Почему во втором примере не сделать функцию incNums const incNums = (arr) => map(arr)(a => [a+1])([]) В этом случае, не будет дополнительной обёртки в виде pipe?
Я думаю потому, что пайп в данном случае является больше целью, если Вы пересмотрите конец видео, то заметите что Соер сказал что "поймёте как с помощью каррированных функций получить пайп", цитата не точная, но смысл такой
Всем доброго времени суток. Спасибо за видео, интересный канал. Подписался. Начал изучать Java, посмотрим как пойдет. На данный момент пишу на 1С, только без негатива пожалуйста..) Всем хорошего просмотра !
Я и без вышки по математике ответы от сервера маплю, просто тут спагетти плюс хаскель какой-то. Для системного программиста норм, мне как фронту спагетти
Умные люди, подскажите, пожалуйста, в каких случаях функциональный стиль программирования эффективнее/выгоднее других? А также, кроме красоты, зачем юзать map, если можно перебрать методом forEach или циклом for?
строгий ответ таков: есть данность а есть заданность. То есть статускво и вектор развития.. Вопросы эффективностидолжны быть не писателя кода вопросами а вопросами писателя компиляторов и разработчиков железа (что и происходит на самом деле если смотреть на бигпикчу эволюции языков). Вопросами писателей кода должна быть логическая строгость со всеми вытекающими из строгости профитами (безопасность, отказоустойчивость. масштабируемость и тд - перечислять можно бескконечно). А теперь ответ конкретнее: >> в каких случаях функциональный стиль программирования эффективнее/выгоднее других? ДОЛЖЕН быть эффективнее любых других. Если это (пока!) не так - это не программиста проблемы, - проблемы компиляторов и железа. И каждый должен заниматься своим делом и стремиться к идеалу при этом
В дополнение к другим ответам: "constraints liberate, liberties constrain" map ограничивает возможности того, что можно сделать с массивом. Значит, читая код и видя map, мы уже точно знаем многое о том, что там происходит. В частности, что длина результирующего массива не изменилась, и что значение следующего элемента изменяется не в зависимости от предыдущего (ну, в js этого свойства нет, ибо map корявенький). Все эти вещи позволяют читать меньше кода (не говоря уж о том, что циклы обычно многословнее). Например, если ты ищешь багу, в результате которой список из функции возвращается короче, чем должен быть, то встретив цикл, его придется читать. А встретив map - не придется.
Начал тебя смотреть когда у тебя на канале было 16 тысяч подписоты. Еще был видос по моему самый первый, где ты говоришь чем веб плох и почему ты не уезжаешь за границу.Растешь!Красава!
То есть ради удобства написания функции мы тащим особенности во вне? Я ожидаю, что a => [a + 1] вернет все-таки массив, но из-за реализации, удобной чуваку, который ее писал, а не мне, придется забивать себе голову особенностями использования.
a => [a + 1] ээто выражение типа 'number => Array', и эта лямбда, получив какую-то жлыгу, вернет массив, не иначе. Но мы передаем ее в качестве аргумента в функцию next, а вот next приняв ее, вернет функцию типа 'Array => Array'. Реализация чего-то-там тут не при чем.
А тепреь вспомните что в императивном стиле достаточно написать for (let i = 0; i < arr.length; i++) arr[i]++; что будет работать куда быстрее, чем рекурсивная функция, бесконечно создающая новый массив. Фп удобно, когда нам нужно сказать не только, что сделать, но еще и сказать, как сделать. Но в абсолют это возводить не стоит. А Соер тем временем стал фп-сектантом и поехал кукухой(
@@АнимусАнанимус Конечно обычный мап легче читается и поддерживается, тут соглашусь. Проблема была в том, что обычный фор был превращен извините хуй знает во что.
@@СергейНовожилов-я6я Может быть полезно, безусловно. Но все таки создание массива каждую итераю лишь для абстракции, куча (((((((((скобочек))))))))) (мы что, превращаем жс в лисп?). И прочее отталкивает меня от класического фп, слишком уродливо, хоть и "самодоказуемо". Не нужно делать из петуха корову, если вам нужно молоко - корову и возьмите
Это, конечно, не map, но и не reduce: если перевести дословно (но с паттерн матчингом) на хачкель (исправив ошибку с merge, которая у Соера конкатенирует списки, а должна соединять голову с хвостом): map [] _ acc = acc map (x:xs) f acc = map xs f (f x : acc) или если выразить через настоящий (труЪ) map: soerMap as f bs = reverse (map f as) ++ bs Хотя конечно, это прям совсем далеко от настоящего map ':) map (+5) [0..3] == [5,6,7,8] soerMap [0..3] (+5) [10..13] == [8,7,6,5,10,11,12,13]
@@АнимусАнанимус стесняюсь спросить... но надо... :) Это я так плохо шарю в синтаксисе js, или это на каком-то другом языке написано, с которым я незнаком? Если на другом, то подскажите на каком, пожалуйста. Появилось ощущение, что я хочу знать этот язык :)
@@АнимусАнанимусну че ты придумываешь? Хоть код бы запустил, что-ли. Ты либо пиши опровержение на языке первоначального примера, либо корректно переводи. А так получается, что ты приводишь пример некорректно переведенный с JS и говоришь что это "совсем не то", конечно не то, ведь ты неправильно перевел. )))) твой пример: map(+5) [0..3] == [5, 6, 7, 8] мой пример: map([0,1,2,3])(a => [a + 5])([]) == [5, 6, 7, 8] можно поспорить насчет аккумулятора, но в том, что это не map, нужны какие-то доводы, я их не увидел.
Нравятся такие видосы побольше бы таких
Куплю попугая и назову его "map(tail(arr))(f)(merge(acc)(f(head(arr))))".
А он еще и будет периодически кричать "error: a is not iterable" 😂
Только Илон Маск может давать имена типа X Æ A-12
Видос огонь!
На практике не доводилось работать с функциональным стилем.
Очень полезно было это узнать
Спасибо большое, я начал понимать зачем нужно карирование, на примере конвейера.
Включил видео, отвернулся, через минуту повернулся, экран размыт, ну думаю пора спать.
История о том, как из JS сделать Common Lisp
Теперь надо на JS написать интерпретатор CL, чтобы на нём написать интерпретатор JS :)
@@Uni-Coder это проще, чем кажется) С учетом синтаксических особенностей лиспа)
Чтобы не писать много скобок уже давно в ФП придумали композицию функций.
Можно даже свою функцию compose реализовать
Соер, респект. Это интересный контент. Спасибо 👍🏼
Заявленные плюсы звучат очень хорошо, и если сама парадигма написания кода способствует этому - это супер круто. Единственное, что меня не порадовало, это итоговое содержание функции map. Серьёзно, тут же можно полдня только разбирать, что написано в этой строке xD
Возможно я неправ, говорю строго на правах мнения, но дело в том, что в данном коде присутствует 4 уровня вложенности вызовов, но кроме того цепочка вызовов ещё и зацикливается: мы вызываем map, чтобы получить функцию, в которой вызывается map, чтобы получить функцию, в которой вызывается map, чтобы получить функцию... Может это дело привычки, и в итоге после некоторой практики такое перестаёт вызывать трудности при чтении, но когда видишь такой код впервые, то не можешь отделаться от мысли, что он ужасен :) Чувство приблизительно то же, что и знакомстве с регулярками, которые изначально воспринимаются не иначе как непонятный набор символов, делающий какую-то магию.
чтобы полюбить рекурсию надо полюбить рекурсию
Ну есть в ФП ещё "частичное применение функции" и "каррирование" которые помогают вызвать функции более привычно, но в то же время использовать преимущества сокращения арности функции
Например
sum(1,3) // 4
sum(1) // a => a + 1
То есть можно написать inc = sum(1) - и передавать ее в конвееры, в всякие Монады.
@@21POPOV map = (arr) => (f) => arr.length === 0 ? [] : merge([f(head(arr))])(map(tail(arr))(f));
Сейчас только подумал, а почему функция не написана так. Я какой-то принцип нарушил, или в видео не оптимально написано (больше вложенность, чем надо)?
Офигенно"
да
тот случай, когда учишь фронтенд и думал, что реакт и редакс сложно))))
В начале видео есть полторы минуты, когда вы просто говорите, можно было, чтобы размытие плавно исчезало перед началом объяснения и плавно возвращалось после, кажется, было бы чуть лучше) А так, спасибо за видео)
12:30 "... в нормальных языках..." ахахах, бедный JS
Он нормальный, ноооормальный, - возглас Джигурда)
позновательно, у меня только один вопрос, зачем ?
конец видео объясняет же зачем такой изврат
ФП - для предсказуемости поведения программы, детерминированности выполнения, лёгкости тестирования. Так же помогает корректно обновлять состояние, особенно в многопоточной среде.
@@drak0an хотелось бы увидеть что то реальное, а не инкрементацию значений списка.
Кайф. Как всегда.
А не приводит ли это к неоправданому выделению памяти? В каждом вызове рекурсивной функции мы в замыкание ложим новый результат tail() и новый результат marge(), и все они живут пока не розвернется весь стек функции.
Это абсолютно не важно, главное чтобы код был написан в *функциональном* стиле, память дело десятое
в третьем примере в реализации pipe надо было sizeOf тоже использовать вместо .length чтобы совсем уж по красоте.
Но в целом вот этот вот висящий аккумулятор у map немного расстраивает. Если там сделать параметр по умолчанию это нормально будет или против парадигмы? или может переобозвать этот map в некий служебный accMap и его обернуть функцией map где уже не надо будет пихать пустой массив т.к. она будет сама вызывать accMap именно с пустым массивом ? тогда и второй пример бы уже выглядел получше.
А так немного приходилось ставить на паузу и до пояснений тратить немного времени чтобы понять что да работает, да правильно и как именно. Такие головоломки сами по себе прикольная штука, это было интересно. Ещё видел на жабаскрипте похожую очень по стилю реализацию прикручивания кеширования (мемоизации если угодно) к произвольной функции, тоже хороший пример. Понятное дело что немного практики и к этому всему можно привыкнуть, но наверное лучше это изучать на каком-нибудь хаскеле где это будет естественно а потом уже привносить в js (на codewars кстати есть какое-то количество задач именно по такому принципу - давайте сделаем на js вот такую штуку по примеру хаскеля, достаточно сложные штуки там есть)
Функциональной парадигме не противоречит назвать эту штуку иначе (например flatMapConcat), а map описать как:
cons map = f => xs => flatMapConcat(xs)(x => [f(x)])([])
Просто в видео не map. В другой ветке комментариев уже выяснили, что это flatMap с последующим concat :D
@@АнимусАнанимус кстати да, мап-то ненастоящий
Ааа, так все эти композиции из функций - они принципиально работают только с функциями с одним аргументом?
Было свободное время, взялся в качестве упражнения для ума разобраться с функционалкой. Автору честь и хвала, такой детальной проработки вопроса я не видел ни у кого (а большенство в лучшем случае останавливалось на чистых функциях). Только если ради каждого забитого в стену гвоздя должны кипеть такие страсти (а уровень абстракций реально зашкаливает)... "Назови мне хоть одну причину"(с) почему это все реально существует, и это двигают? Заговор мирового правительства? (желательно с той же степенью детализации и убедительности). С таким счетом на табло при сравнении кода для решения задачи, команду, за которую играл автор, пора разгонять
04:30 Так хочется сделать Array.prototype.map = null :)))
P.S. Про читаемость кода, я понял, что lst это list, а не last, только после того, как было озвучено, что "всё есть список".
Концепция, на вид, крутая - обязательно попробую.
Есть ощущение, что третья по счету реализация (func_curry.js) - скорее reduce, нежели map. Или я заблуждаюсь?
Первая задачка, решение на Common Lisp:
(defun list-inc (lst)
(if (null lst)
()
(append (list (1+ (car lst)))
(list-inc (cdr lst)))))
Подобные издательства над js можно пройти на курсах hexlet. Когда решал задачи мозг разрывался.
Я после их курсов проникся ФП и начал писать на Clojure). И да, задачки действительно очень мозголомные.
это точно ))
Топ!!! Побольше подобного контента. Посоветуйте книгу по ФП.
Почему во втором примере не сделать функцию incNums
const incNums = (arr) => map(arr)(a => [a+1])([])
В этом случае, не будет дополнительной обёртки в виде pipe?
Я думаю потому, что пайп в данном случае является больше целью, если Вы пересмотрите конец видео, то заметите что Соер сказал что "поймёте как с помощью каррированных функций получить пайп", цитата не точная, но смысл такой
🔥🔥🔥🔥🔥
Осталось понять, как это всё понять.
Есть книга про котлин и фп. Рекомендую, очень много примеров. С самого начала все хорошо разбирается. Рассматривается написание всего приложения в фс.
О, а можешь, пожалуйста, название подсказать?
Как называется?
@@ruslanvolovik2745 могу предположить, что Functional Programming in Kotlin от manning
Всем доброго времени суток. Спасибо за видео, интересный канал. Подписался.
Начал изучать Java, посмотрим как пойдет. На данный момент пишу на 1С, только без негатива пожалуйста..)
Всем хорошего просмотра !
Это не Java а JavaScript это совсем разные языки, общее в них только 4 буквы
бомба
Первое правило функционального программирования на JS - не использовать JS
Бред
очень сложный код, не мой уровень точно
На самом деле нет, если есть базовое университетское математическое образование. Что как говорилось неоднократно необходимо для работы в it
Я и без вышки по математике ответы от сервера маплю, просто тут спагетти плюс хаскель какой-то. Для системного программиста норм, мне как фронту спагетти
Эх, столько нужно выучить чтобы посчитать список чисел) Пойду лучше на яве покодю.
Умные люди, подскажите, пожалуйста, в каких случаях функциональный стиль программирования эффективнее/выгоднее других? А также, кроме красоты, зачем юзать map, если можно перебрать методом forEach или циклом for?
В многопоточности, например.
Map и forEach решают разные же задачи)
строгий ответ таков:
есть данность а есть заданность. То есть статускво и вектор развития.. Вопросы эффективностидолжны быть не писателя кода вопросами а вопросами писателя компиляторов и разработчиков железа (что и происходит на самом деле если смотреть на бигпикчу эволюции языков). Вопросами писателей кода должна быть логическая строгость со всеми вытекающими из строгости профитами (безопасность, отказоустойчивость. масштабируемость и тд - перечислять можно бескконечно).
А теперь ответ конкретнее:
>> в каких случаях функциональный стиль программирования эффективнее/выгоднее других?
ДОЛЖЕН быть эффективнее любых других. Если это (пока!) не так - это не программиста проблемы, - проблемы компиляторов и железа.
И каждый должен заниматься своим делом и стремиться к идеалу при этом
В дополнение к другим ответам:
"constraints liberate, liberties constrain"
map ограничивает возможности того, что можно сделать с массивом. Значит, читая код и видя map, мы уже точно знаем многое о том, что там происходит. В частности, что длина результирующего массива не изменилась, и что значение следующего элемента изменяется не в зависимости от предыдущего (ну, в js этого свойства нет, ибо map корявенький).
Все эти вещи позволяют читать меньше кода (не говоря уж о том, что циклы обычно многословнее).
Например, если ты ищешь багу, в результате которой список из функции возвращается короче, чем должен быть, то встретив цикл, его придется читать. А встретив map - не придется.
@@КаримАль-Савади очень сомнительное преимущество.
Только всё будет тормозить)
Почему же?
@@rotrhino потому что мы копируем массив
Каждый рекурсивный вызов в данных примерах создаёт массив.
Это далеко не бесплатная операция.
@@MrVintarb фанатам ФП это неинтересно
Это проблемы js-а уже😏
Начал тебя смотреть когда у тебя на канале было 16 тысяч подписоты. Еще был видос по моему самый первый, где ты говоришь чем веб плох и почему ты не уезжаешь за границу.Растешь!Красава!
Без паттерн матчинга выглядит не очень )))
Соглы. Но если паттернматчить по Чёрчу, то не будет оптимизации хвостовой рекурсии :(
const destroyArray = ifEmpty => ifCons => arr =>
arr.length === 0 ? ifEmpty : ifCons(head(arr))(tail(arr))
const map = f => destroyArray ([])(x => xs => cons(f(x))(map(f)(xs)))
Кстати, самое "мясо" Соер решил не показывать. Это списки в кодировке Чёрча:
const Empty = ifEmpty => ifCons => ifEmpty
const Cons = x => xs => ifEmpty => ifCons => ifCons(x)(xs)
const toArray = xs => xs ([])(y => ys => [y,...toArray(ys)])
const fromArray = xs => xs.reverse().reduce((ys,y) => Cons(y)(ys), Empty)
const foldl = f => acc => xs =>
xs(acc)(y => ys => foldl(f)(f(acc)(y))(ys))
const map = f => xs =>
xs(Empty)(y => ys => Cons(f(y))(map(f)(ys)))
let myList = Cons(1)(Cons(2)(Cons(3)(Empty)))
Хотя это не кажется таким хардкорным, как тот безумный map в конце видео :D
Это конечно круто и очень интересно, но с точки зрения производительности в js просто ужас
А почему?
@@Brother_raccoon ну дак стек не резиновый
То есть ради удобства написания функции мы тащим особенности во вне?
Я ожидаю, что a => [a + 1] вернет все-таки массив, но из-за реализации, удобной чуваку, который ее писал, а не мне, придется забивать себе голову особенностями использования.
a => [a + 1] ээто выражение типа 'number => Array', и эта лямбда, получив какую-то жлыгу, вернет массив, не иначе.
Но мы передаем ее в качестве аргумента в функцию next, а вот next приняв ее, вернет функцию типа 'Array => Array'.
Реализация чего-то-там тут не при чем.
А тепреь вспомните что в императивном стиле достаточно написать
for (let i = 0; i < arr.length; i++) arr[i]++;
что будет работать куда быстрее, чем рекурсивная функция, бесконечно создающая новый массив.
Фп удобно, когда нам нужно сказать не только, что сделать, но еще и сказать, как сделать.
Но в абсолют это возводить не стоит.
А Соер тем временем стал фп-сектантом и поехал кукухой(
У Соера на канале акцент на философию - чем он своеобразен и интересен. Я думаю я не один такой ценитель поехавших кукух ;)
на простых числах может и норм. Но когда есть сложные структуры данных, с кучей разных настраиваемых фильтров, фп может быть полезно.
Ну да,
for (let i = 0; i < arr.length; i++) arr[i]++;
ведь легче читать и поддерживать, чем
arr.map(x => x + 1)
И кто здесь сектант-то? ':)
@@АнимусАнанимус Конечно обычный мап легче читается и поддерживается, тут соглашусь.
Проблема была в том, что обычный фор был превращен извините хуй знает во что.
@@СергейНовожилов-я6я Может быть полезно, безусловно. Но все таки создание массива каждую итераю лишь для абстракции, куча (((((((((скобочек))))))))) (мы что, превращаем жс в лисп?).
И прочее отталкивает меня от класического фп, слишком уродливо, хоть и "самодоказуемо".
Не нужно делать из петуха корову, если вам нужно молоко - корову и возьмите
непонятно, почему map стал каким-то убогим редюсом. ну и вообще кашу какую-то понаписал.
Это, конечно, не map, но и не reduce:
если перевести дословно (но с паттерн матчингом) на хачкель (исправив ошибку с merge, которая у Соера конкатенирует списки, а должна соединять голову с хвостом):
map [] _ acc = acc
map (x:xs) f acc = map xs f (f x : acc)
или если выразить через настоящий (труЪ) map:
soerMap as f bs = reverse (map f as) ++ bs
Хотя конечно, это прям совсем далеко от настоящего map ':)
map (+5) [0..3] == [5,6,7,8]
soerMap [0..3] (+5) [10..13] == [8,7,6,5,10,11,12,13]
@@АнимусАнанимус стесняюсь спросить... но надо... :) Это я так плохо шарю в синтаксисе js, или это на каком-то другом языке написано, с которым я незнаком? Если на другом, то подскажите на каком, пожалуйста. Появилось ощущение, что я хочу знать этот язык :)
@@nikolaymatveychuk6145 это Haskell :)
@@АнимусАнанимус спасибо
@@АнимусАнанимусну че ты придумываешь? Хоть код бы запустил, что-ли.
Ты либо пиши опровержение на языке первоначального примера, либо корректно переводи. А так получается, что ты приводишь пример некорректно переведенный с JS и говоришь что это "совсем не то", конечно не то, ведь ты неправильно перевел. ))))
твой пример: map(+5) [0..3] == [5, 6, 7, 8]
мой пример: map([0,1,2,3])(a => [a + 5])([]) == [5, 6, 7, 8]
можно поспорить насчет аккумулятора, но в том, что это не map, нужны какие-то доводы, я их не увидел.
Это конечно прикольно, но бесполезно
Жду тухлые помидоры
А можно без размытия экрана? Раздражает