• Два года с Dart: о том, как мы пишем на языке, который ежегодно «хоронят» (часть 2)
    0

    Может сразу и Kotlin.js захватите?

  • React + mobx путь с нуля. Mobx + react, взгляд со стороны
    0

    Знаете такие же, но не заброшенные?

  • React + mobx путь с нуля. Mobx + react, взгляд со стороны
    0

    Не уверен, но вроде то, что вам нужно: gritzko/swarm, josephg/ShareJS.

  • Как сделать сайдбар за 5 строк кода
    +2

    И совсем не дорого)

  • Управляем состоянием приложения с помощью Vuex
    0
    На GC плохо влияет большое число долгоживущих объектов (создать и тут же уничтожить объект — сейчас почти ничего не стоит)

    Откуда ты узнаёшь всё это?)
    Что значит долгоживущих? Достаточно чтобы он просто жил и это уже будет влиять на GC или влияет его удаление после того как он долго пожил?

  • Умер ли MVC для фронтенда?
    +1

    Блин, да откуда берутся эти люди, утверждающие, что MVC не однонаправленный? Разница между MVC и Flux лишь в том, как взаимодействуют контроллер и модель. В MVC контроллер напрямую ковыряется в модели, в Flux он лишь генерит событие с данными, а модель уже сама решает как эти данные разложить по себе. Где тут изменение потока?

  • Firebase: прощание с иллюзиями
    0

    С ArangoDB не приходилось сравнивать? Сейчас между ними выбираю.

  • Пишем простое приложение на React с использованием библиотеки cellx
    0
    Между скоростью и корректностью, лучше выбирать корректность
    Если потомок имеет ссылку на родителя, то в чём проблема пометить родителя устаревшим

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


    А в чём практический смысл пассивного режима? Ведь он приводит к тому, что и вычисления все нужно проводить

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

  • Пишем простое приложение на React с использованием библиотеки cellx
    0
    … А если В не изменит значение, то С не будет перевычисляться ...

    ну да, то что там защита от лишних расчётов это понятно. Я экспериментировал с таким вариантом, не ради другого поведения, а интересно было, что по скорости из него можно было выжать. Получалось не очень, много потерь на обходе для инвалидации ячеек перед их вычислением. Сейчас у меня похожим образом работает пассивный режим, но без этого обхода (его и не может быть, сам смысл пассивного режима в отсутствии ссылок от родителей к потомкам, чтобы GC мог забирать такие ячейки, а значит и обходить просто не по чему). Сделано немного иначе: есть общая для всех переменная, которая при каждом проходе расчёта инкрементится и все попавшие в этот обход ячейки получают это значение. У оставшихся ячеек это значение уже отстаёт, это и делает их устаревшими. Минус в том, что устаревшими могут случайно становиться лишние ячейки, ну и естественно случаются лишние расчёты, но тут уже ничего не поделать, при отсутствии ссылок обойти и точно пометить кто устарел, а кто нет, не выйдет. Для пассивного режима этого хватает, для основного конечно не подойдёт.

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Непонятно почему изменился порядок расчёта после push: http://jsbin.com/xebozudala/1/edit?js,console? Видимо ещё хитрее)

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Кажется разобрался, когда меняется A все кто от него зависит помечаются как возможно требующие перерасчёта, дальше читается самый вложенный, в A -> B -> C это C, в нём сперва срабатывает его console.log и следом читается B, B при чтении понимает, что он возможно устарел и не отдаёт запомненное значение, а тоже начинает перерасчитываться, срабатывает уже его console.log. В общем, всё работает подобно обычным функциям. Так можно сказать? Или как-то ещё хитрее?))

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    @vintage, я правильно понял, что в схеме A -> B -> C при изменении A сперва вычислится C, потом B и если значение B при этом изменится, то C посчитается ещё один раз?
    Добавил чтобы sub возвращал выводимое значение, появилось что-то лишнее: http://jsbin.com/mabozejudi/1/edit?js,console

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    А что за ситуация в которой понадобилось при каждом расчёте создавать ячейки? Это какая-то реальная ситуация или просто ради эксперимента?

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Вот графически схема зависимостей: https://yadi.sk/i/6pID2uk7xFZ7s

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Ну смотрите, есть три ячейки зависимые в таком порядке: A -> B -> C, изменяем A. Кто должен сперва вычислится B или C? Если сперва вычислится C, а потом B, то C прочитает устаревшее значение от B и вычислится неправильно. Примерно тоже самое и здесь. Проблема на самом деле намного хитрее, в статье про атомы vintage толково её описал (начиная с 2. Каскадное непротиворечивое обновление значений)


    пока до foo => oof

    в выводе сейчас вроде всё как надо.

  • Пишем простое приложение на React с использованием библиотеки cellx
    0
    тот самый лишний

    неа, он обновился первым, потому что root зависит от него, он (sub) должен вычислиться раньше чтобы дать ему (root) актуальное значение (в данном случае оно не используется), иначе сам root вычислится от старого значения sub, а только потом sub обновится.


    А где теперь свежие значения

    foo запомнился в замыкании при первичном создании ячейки, тут уже проблема проброски данных (и вполне решаемая), а не РП.

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Да не, нет всё таки проблемы)) Ячейки создаются в самой формуле при каждом её расчёте, это же вообще никак не нормально. Я даже не знаю к какой жизненной ситуации это можно прилепить и даже если найдётся такая ситуация, то там такое поведение скорее всего окажется как раз ожидаемым. Вот, я сделал что бы ячейки от первого расчёта подцеплялись в последующих: http://jsbin.com/hokurejije/1/edit?js,console, теперь всё правильно)

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    А точно ли он лишний? Реакт ведь не создаёт новые инстансы компонентов для элементов списка при повторном рендере, а как-то совмещает их с предыдущими. Если так, то лишний кто-то из двух последних, а другой из них должен начинаться с 'foo'. Вроде так, нет?

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Я правильно понимаю, что в root под подпиской на items[0].value подразумевается подписка на сам список?

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    На самом деле всё правильно, rootLevel читает items[0].value и следовательно подписывается на него, если всё же нужно неправильно, то нужно как-то прочитать его без автоматически срабатывающей при этом подписки. Раньше для этого можно было при чтении передать false, потом я убрал эту возможность, вроде нигде не пригождалась, поэтому сейчас только читать скрытое поле: http://jsbin.com/cubipigaqe/1/edit?js,console
    Могу вернуть опцию, не проблема)

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Блин, root: bar лишний))

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Ага, понял, в прошлый раз не правильно написал, вот так должно быть: http://jsbin.com/fuhepiboci/edit?js,console
    Вывод:


    root: foo
    item: foo + foo
    item: foo + bar
    root: bar
    item: bar + bar

    Первые три — инициализация, остальное, вроде то, что ожидается)

  • Пишем простое приложение на React с использованием библиотеки cellx
    0
  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    Только реакт.

  • Пишем простое приложение на React с использованием библиотеки cellx
    0

    http://jsbin.com/vexugulala/1/edit?js,console


    // root: foo
    // item: foo + foo
    // root: bar
    // item: bar + bar
  • Пишем простое приложение на React с использованием библиотеки cellx
    0
    услышать об опыте Riim

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


    создатели реакта много раз уже говорили, что это не проблема, это by design и им всё нравится

    не правда, им это нифига не нравиться, они просто не могут с этим что-то сделать, так как там реально тупик, что только я не пробовал, в том числе вообще уходить от виртуальной составляющей, продублирую здесь свой другой комментарий:
    ----
    Плюс у меня есть свои реализации как VDOM, так и патчера без V составляющей. Мне тоже вся эта идея изначально очень нравилась и нахватавшись всяких кейсов вроде описанного выше я попытался исправить их в своём варианте, поняв, что это нормально сделать не получается, начал пилить morph-element, идея была такой: сравнение строк — идеально быстрая операция, причём не зависит от длинны строки, даже при очень длинных и почти совсем одинаковых строках сравнение происходит сверхбыстро где бы не находилось различие. Дальше вместо подстановки разметки дочернего компонента за счёт вызова его как функции, родитель должен был генерировать кастомные элементы разметка которых разворачивалась уже в attachedCallback (ныне connectedCallback) (он синхронный, полифилится на MutationObserver не синхронно, но это тоже удалось решить). Что это давало? Получалось, что каждый компонент генерирует только свой собственный контент, но не контент потомков, то есть при первой генерации можно просто запомнить эту строку и при последующей сравнивать не два огромных объекта со всем контентом потомков, а две относительно короткие строки, что в 100500 раз быстрее. Ну и естественно не перегенерировать контент потомков при равности контента родителя с предыдущим вариантом. То есть лишняя перегенерация всё же происходила (родитель), но уходила не на всю глубину, а максимум на один уровень. Такой подход решал некоторые проблемы VDOM, но не все и в целом патчинг с реального DOM (без виртуальной составляющей) всё же получается медленней, как бы я не пытался его оптимизировать и что бы там не говорил разработчик оригинального morphdom.
    В общем, пока я здесь в тупике, а все другие варианты, что я вижу, предлагают костыли вроде:


    добавляем dirty флаг

    используем внутренние часики и проверяем по дате модификации

    А хочется то нормальный фреймворк, который позволяет просто спокойно кодить не расставляя эти самые костыли по всему коду.
    Сейчас я для себя пришёл к выводу, что VDOM — тупиковая идея, но я не исключаю, что ошибаюсь и возможно ещё вернусь к ней))
    Пока вернулся к точечным биндингам, тут тоже не всё в идеале, но всё же явно лучше.
    ----


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


    Ещё бы хорошо получить ответ на мой первый комментарий.
    А теперь представим, что React, в следующей версии меняет логику вызова render

    В этом случае сломается не только cellx-react, многое должно сломаться и вряд ли они будут так круто что-то менять.

  • Пишем простое приложение на React с использованием библиотеки cellx
    +2
    Что больше всего раздражало в Нокауте: конвертация и расширение загруженного JSON-a в Observable. Иначе не работали даже примитивные формы редактирования. Это проникновение библиотеки на уровень, где ее не должно быть, и без нее можно обойтись, когда постоянно нужно помнить, что у тебя — данные, а что — модели, и почему же, все-таки, они не могут быть одним и тем же.

    Да! Полностью согласен. Проблема действительно есть, детально её можно разложить по следующим пунктам (первый малость не в тему, но он тоже нужен):


    1) Ренейминг. Сервер присылает fname и lname, а я хочу нормальные firstName и lastName, я не хочу подстраиваться под правила нейминга на сервере и я не хочу на каждый чих идти и договариваться с кем-то из беков, пусть даже они сразу идут навстречу.


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


    function setData(instance, data) {
        for (let name in data) {
            instance[name] = data[name];
        }
    }

    Но не всё так просто, далеко не всегда данные в полях просто числа и строки, там часто лежат экземпляры других типов. И вот красивые вызовы типа setData(user, usedData) начинают превращяться в какой-то такой ужас:


    setData(user, {
        ...userData,
        father: setData(father, userData.father)
    });

    Причём всё это происходит постоянно и в заметно большем колличестве.


    3) Нужно постоянно конвертировать массивы и хеши в их наблюдаемые аналоги (cellx.list и cellx.map). Этот пункт похож на предыдущий, их можно слить в один и общий пример получается примерно таким:


    setData(user, {
        ...userData,
        friends: cellx.list(userData.friends.map(friendData => setData(new User(), friendData)))
    });

    4) Почему в предыдущем пункте мы просто создаём новые экземпляры друзей? Что если данные какого-то друга уже запрашивались, где-то используются (есть экземпляр этого пользователя) и новые данные нужно слить в уже существующий экземпляр? Например, есть основные данные пользователя для его маленькой карточки, а остальные данные запрашиваются отдельно при заходе на его анкету. Что-то подобное происходит постоянно и для многих типов приложения где-то в хранилище приходиться заводить хешики в которые по айдишникам складывать существующие экземпляры, а потом постоянно писать так:


    setData(store.userMap[userData.id] || (store.userMap[userData.id] = new User()), userData);

    Это тоже бесит.


    5) Кто сказал, что всегда нужно просто брать данные и тупо записывать их в какие-то поля? Что если записывать их нужно как-то иначе? Идеальный пример тут — бесконечная подгрузка списков — нужно не просто заменить существующий список новым, а дополнить существующий список новыми записями.


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


    Теперь про решение, да, редакс с его чистыми данными по большей части не требует никаких решений и в этом его плюс, но он же заставляет отказаться от плюшек реактивного программирования. Если перечисленные проблемы реально автоматизировать до уровня когда они не будут напрягать, то почему бы так и не сделать, сохранив при этом возможность использовать РП.
    Я просто расскажу как всё устроено в текущем проекте, это уже не первая моя попытка всё это решить и пока похоже наиболее удачная.
    По пунктам, первый пункт вполне актуален и при использовании редакса, и сразу вопрос: почему фиксить нейминг нужно именно при конвертировании данных в типы или, в случае редакса, при их записи в хранилище? В любом (или почти любом) приложении есть слой доступа к удалённым данным, он обычно не обсуждается при обсуждении архитектуры, но он есть, у меня он называется proxy (чаще называют providers или как-то так), почему бы не фиксить нейминг в нём? Он лучше для этого подходит, данные он при этом будет возвращать по прежнему чистые, никаких типов, кроме того, сразу после этого слоя можно вообще забыть о существовании какого-то другого нейминга. У меня в папке src/proxy лежит папка converters в которой файлики типа convertUserData.js, через которые пропускаются успешные ответы сервера. Типичное содержимое:


    // @flow
    
    export default function convertContactMediaCompanyData(data: {
        ID: string,
        format: { name: ?string },
        name: ?Array<string>
    }) {
        return {
            id: data.ID,
            formatName: data.format.name,
            names: data.name && data.name.length ? data.name : null // Массив имён, на сервере не любят добавлять `s` в конце имён коллекций
        };
    }

    Если есть сложные подданные, то создаётся ещё подобный файл и используется в текущем:


    tags: data.tag && data.tag.length ? data.tag.map(convertContactTagData) : null,

    Что бы не дублировать типизацию в текущем конвертере поле tags можно описать как Array<Object>, а уже в convertContactTagData расписать подробнее.
    При таком подходе удаётся убить сразу двух зайцев: пофиксить нейминг и, используя модуль babel-plugin-tcomb сразу провалидировать ответ сервера, тайпскрипт тут не подойдёт, он верит программисту, а при работе с сервером программист не может верить сам себе, tcomb же подставляет реалтаймовые проверки. Это позволяет отлавливать косяки данных сервера не где-то в слое представления и пол дня тратить на понимание причины, а в самом их корне. Конечно же сборка настраивается так, что реалтаймовые проверки подставляются только для dev-сборки (иначе продуктовый бандл сильно раздувается, а для dev-версии это норм и этого достаточно для получения нужного результата).


    Дальше следующие три пункта я решаю модулями @riim/statex и @riim/typify-data. Я не планировал их в ближайшее время кому-то показывать и там нет описания, поэтому опишу здесь. По сути это очень продвинутая версия setData.
    new StateX({}) создаёт корневое хранилище, первый аргумент — карта имён на классы типов, на примере будет понятнее:


    import StateX from '@riim/statex';
    import App from './types/App';
    import User from './types/User';
    // ...
    
    let state = window._state = new StateX({
        App,
        User,
        // ...
    });
    
    export default state;

    Зачем карта будет понятно чуть позже. Дальше метод push — принимает чистые данные (ну почти, typifyData их чуть-чуть портит) и обходя их рекурсивно решает пункты 2-4:


    • массивы оборачивает в cellx.list
    • в объектах ищет поле __type, если нет, то оборачивает в cellx.map, если есть, находит в карте переданной при вызове StateX соответствующий данным класс. Хешики из пункта 4 строятся всегда автоматически для каждого типа: смотрит поле id, ищет его в хешике, при отсутствии создаёт инстанс и запоминает там.
    • если id нет, значит этот тип единичный в приложении, скажем это тип описывающий раздел приложения "Настройки".

    Метод get — возвращает инстанс по типу и, если есть, айдишнику:


    this.user = state.get('User', this.props.userId);

    id приходит с сервера, теперь остаётся только добавить __type. Добавлять его в конвертерах мне кажется не совсем хорошо, proxy должен возвращять чистые данные, а __type — это метаинформация в данных о том, что это за данные. Добавлять их как-то так:


    store.push({
        __type: 'User',
        ...userData
    });

    тоже не хочется, при большей вложенности будет неудобно:


    store.push({
        __type: 'User',
        ...userData,
        friends: userData.friend.map(friendData => {
            __type: 'User',
            ...friendData
        })
    });

    по сути это возвращение к тому от чего только что ушли. И вот тут как раз нужен typifyData, он принимает первым параметром совсем сырые данные и по второму аргументу добавляет поле __type в них, предыдущий пример на нём:


    store.push(typifyData(userData, { '': 'User', 'friends': 'User' }));

    "Проваливание" в массивы происходит автоматически и как-то отдельно в ключе передаваемого объекта не описывается.
    Можно ещё посложнее:


    store.push(typifyData(userData, {
        '': 'User',
        'tags': 'UserTag',
        'friends': 'User',
        'friends.tags': 'UserTag'
    }));

    Ну и последний пункт. Опять же первая мысль, придумать ещё что-то типа typifyData добавляющее метаинформацию к данным о том как их использовать, а #push научить этому. Плохо то, что вариантов использования очень много, всему #push не научишь, тут можно решить намного красивее и универсальнее. В статье где сравнивается cellx-vs-mobx есть про опцию put, вот она то и нужна, просто покажу как можно решить с бесконечной прокруткой:


    @observable contacts = void 0;
    @observable total = void 0;
    
    @observable({
        put(value, push) {
            if (value) {
                if (this.contacts) {
                    this.contacts.addRange(value);
                } else {
                    this.contacts = value;
                }
            }
    
            push(value);
        }
    }) nextContacts = void 0;

    То есть на уровне конвертеров переименовываем contacts в какой-нибудь nextContacts, в классе же поле nextContacts сохраняет их себе (push(value)), но перед этим дописывает в соседний contacts. Тут можно и без observable свойства, nextContacts может быть обычным сеттером, но тогда от него нельзя будет вычислять другие ячейки, мало ли зачем такое понадобиться. Подобно можно любым образом нестандартно записывать что-то в поле через StateX#push.


    Вот как-то так я и упарываюсь :). Знаю, что это Хабр и сейчас набежит куча экспертов, которые разнесут всё в пух и прах и смешают с авном, не стоило это писать, я просто вижу как это работает, кода получается явно меньше чем даже с редаксом, нет уже этих многоуровневых { ...data, prop: { ...subdata, prop: { ...subsubdata, ... } } } и вся эта ранее постоянно напрягающая конвертация данных уже вообще не заметна, плюс плющки РП и хорошая производительность.

  • Выбираем состав изоморфных React-приложений на следующие 12 месяцев
  • Matreshka.js 2 — самый простой фреймворк во Вселенной
    –2
    чтоб могли посмотреть все пользователи

    если бы я хотел показать всем, я бы наверно не стал отправлять в ЛС. Это как минимум не красиво с вашей стороны. Не считаете так?


    Вдруг разработчики в яндексе действительно боги, и смогли закодить 10+ кнопочек и переключателей без модных Ангуляра и Реакта

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

  • ES6 const это не про иммутабельность
    –2
    Команда всегда состоит из «какой-то баран»

    у вас видимо всегда, ну ок, свои аргументы я привёл, с этим плюсом const тоже согласен, кому интересно почитают, взвесят и решат для себя (а ещё лучше попробуют). Что-то новое здесь уже вряд ли прозвучит, а препираться с теми кто принципиально не готов признавать альтернативы смысла нет, так что всем удачи.

  • ES6 const это не про иммутабельность
    0

    Я именно так и понял, аргументы к этому выше.

  • ES6 const это не про иммутабельность
    –2
    не используй

    не тыкайте мне, пожалуйста.


    С опытом понимание само придет

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

  • ES6 const это не про иммутабельность
    –2
    то вот вам пример

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


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

    обажаю хабр, здесь всегда найдётся чудо которое в ситуации где можно спокойно сказать "да чувак, в твоих аргументах что-то есть и твой вариант похоже имеет право на жизнь" и на этом спокойно разойтись, будет до хрипоты в голосе усираться и доказывать, что нет!, только его вариант единственно правильный и никак иначе. А когда кончаться аргументы скатится на банальное язвление. Язвить я умею не хуже вашего, поэтому если больше нет интересных аргументов, то предлагаю не трахать друг другу мозг и на этом разойтись.

  • ES6 const это не про иммутабельность
    –1
    думать нужно всегда

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

  • Matreshka.js 2 — самый простой фреймворк во Вселенной
    –1

    Ну не знаю, вдруг окажется, что я нуб и всё это на матрёшке за пару недель делается)) Позор-то какой будет) Отправил в ЛС.

  • Facebook и Google выпустили Yarn, новый менеджер пакетов для JavaScript
    –3

    Точняк))

  • Facebook и Google выпустили Yarn, новый менеджер пакетов для JavaScript
    0

    Похоже фейсбук решил окончательно захватить мир. Помнится лет 5 назад в плане опенсорса я вообще ничего о них не слышал, а сейчас React, GraphQL, Atom, Yarn, ещё целая куча всякого интересного. Молодцы в общем))

  • ES6 const это не про иммутабельность
    +1

    Ну если у нового программиста часто будут такие проблемы, то с ним в принципе много проблем будет, const тут мало что изменит. В целом да, можно засчитать как небольшой плюс const)).

  • Matreshka.js 2 — самый простой фреймворк во Вселенной
    0

    Оценили? Реально считаете возможным сделать что-то подобное на матрёшке?

  • ES6 const это не про иммутабельность
    +2

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