четверг, 31 июля 2014 г.

Ссылка. ОЧЕНЬ. Контракты vs. Монады?

http://sergeyteplyakov.blogspot.ru/2014/07/contracts-vs-monads.html#more

Процитирую, то, что мне ОСОБЕННО нравится:

"

Три уровня обороны

Когда мы говорим о продакшн коде статически-типизированного мейнстрим языка, у нас есть три уровня обороны, которые защищают нас от багов и делают наш код более выразительным и сопровождаемым:
  1. Система типов
  2. Контракты
  3. Юнит-тесты
Чем "выше" линия обороны, тем раньше она срабатывает и "докладывает" нам о возможной проблеме в коде. Если мы что-то можем выразить с помощью системы типов, то именно с ее помощью мы должны выражать свои намерения. Именно поэтому в С++ обязательные входные значения передаются по константной ссылке, необязательные входные значения – по константному указателю и т.п.
Аналогично, если методу для успешной работы нужен список, то обычно нет смысла приниматьobject и кастить его к списку, внося потенциальную ошибку времени исполнения.
API класса/метода должен быть таким, чтобы его легко было пользовать правильно и сложно использовать неправильно!
Но что, если мы не можем выразить что-то через систему типов (ибо она недостаточно выразительна)? Например, в языке C# мы не можем выразить, что аргумент метода ссылочного типа является обязательным, поскольку любой ссылочный тип может принимать значение null. Иногда, когда это возможно, мы можем заменить его на тип-значение (value type) и уйти от проблемы, но далеко не всегда это возможно (ну и вы же помните о проблеме изменяемых значимых типах, правда?)
Когда система типов бессильна, нам на помощь приходят контракты: инструмент, который помогает выразить наши намерения в более явном виде. Как выразить с помощью системы типов, что метод Addинтерфейса ICollection<T> может не добавлять еще один элемент, а метод Add любой реализации класса IList<T> обязана это сделать? (Помните обсуждение этого вопроса в статье "Принцип замещения Лисков и контракты"?).
Контракт располагается в самом методе и формально является частью его "сигнатуры", что делает его (метод) более описательным. При этом некоторые нарушения контрактов могут детектиться на этапе компиляции (с помощью статик-чекера, или инструментов, таких как R#), а некоторые – на этапе исполнения.
Помните о том, что код сам по себе не может быть корректным, или не корректным. Понятие корректности (т.е. наличие ошибки в коде) возможен лишь при наличии намерений этого кода (известного в быту под названием "спецификация", которая может быть формальной или неформальной).
Контракты по своей природе тоже не могут выразить все тонкости ожидаемого поведения. Когда их выразительности становится недостаточно, нам на помощь приходит следующая линия обороны – юнит-тесты. Так, например, в контрактах невозможно выразить, что третьим числом Фибоначчи будет 2, или что при вызове метода Save вью-модели будет вызван метод SaveEmployee нашего сервиса.
Тест еще сильнее отдален от кода, ведь он находится во внешней сборке, так что его связь с продашкн кода становится менее очевидной, а время обнаружения ошибки увеличивается со времени компиляции до времени запуска тестов. При этом роль тестов пересекается с ролью контрактов: тесты также выражают ожидаемое поведение, несоответствие которому говорит о наличии в коде багов (или же говорит об изменении ожидаемого поведения, которое еще не было отражено в тесте)."

и ЕЩЁ:

"Наши инструменты не должны скрывать ошибки, а должны следовать идиоме Fail Fast и бросать исключения, или прерывать исполнение как можно раньше (помните, нарушение контрактов может вести себя аналогично нарушению Debug.Assert!).
Почему нужно следовать идиоме Fail Fast? Чтобы избавиться от того самого эффекта бабочки, когда изменение в одной части системы проявится на несколько слоев ниже, или на несколько модулей правее, от места возникновения ошибки! (Мы просто будем сидеть и думать, а какого лешего при обработке этого запроса мы сохранили null в этом поле базы данных?)"

КАК МОЖНО РАНЬШЕ!

ПОВТОРЮ!

КАК МОЖНО РАНЬШЕ!

и ещё раз ПОВТОРЮ!

КАК МОЖНО РАНЬШЕ!

Да..

И ещё...

(Надеюсь что меня не упрекнут в излишнем цитировании)

Вот:
"

Так что насчет Контракты vs. Монады?

Да, кажется я несколько отвлекся от исходной темы. Так вот, проясню в качестве заключения: всяческие монады, и методы расширения для упрощения обхода графов объектов упрощают control flow, но они не имеют никакого отношения к тому, что делают контракты!
Контракты призваны придать дополнительный смысл безликим методам там, где система типов языка не справляется, и четко определить, за что отвечает каждая из сторон, и кто виноват, если эти соглашения не выполняются. Контракты – ловят ошибки, а монады и оболочки за счет своей выразительности стараются свести их количество к минимуму. Но эти подходы не конкуренты, каждый из них отлично подходит для решения своих проблем.
Не нужно с помощью монад прятать ошибки, возвращая None на переданный null, а контракты не нужно использовать для валидации данных от пользователя. Не предавайтесь культу карго, и используйте инструмент по назначению!"

2 комментария:

  1. Не очень люблю Теплякова. Глубины нет. Может генерировать огромные объемы текста о том, что "лучше быть здоровым и богатым, чем бедным и больным". С такой постановкой вопроса конечно не согласиться трудно, но и не понятно зачем читать раз за разом то, что уже было пережевано всеми, кому не лень. Извините.

    ОтветитьУдалить
  2. Ну я вот "не пережёвывал" - сразу "вопли" - "дайте код", "объясните подробнее".

    Короче....

    Обвинять Теплякова "в отсутствии глубины" - это ИМХО - "что-то"..

    Извините.

    ОтветитьУдалить