[программистское]
Oct. 20th, 2008 10:06 pmграмотный текст обо всём понемногу: http://gaperton.livejournal.com/23719.html?style=mine
единственное, что у меня вызывает сомнения, это полезность ассёртов. написать assert(A) немногим быстрее, чем if (!A) throw new AException, однако же второе лучше во всех отношениях.
единственное, что у меня вызывает сомнения, это полезность ассёртов. написать assert(A) немногим быстрее, чем if (!A) throw new AException, однако же второе лучше во всех отношениях.
(no subject)
Date: 2008-10-21 05:47 am (UTC)(no subject)
Date: 2008-10-21 09:37 am (UTC)(no subject)
Date: 2008-10-21 06:19 pm (UTC)(no subject)
Date: 2008-10-22 09:03 am (UTC)(no subject)
Date: 2008-10-22 09:15 pm (UTC)Re: это чушь собачья
Date: 2008-10-23 08:38 am (UTC)(no subject)
Date: 2008-10-23 06:47 pm (UTC)(no subject)
Date: 2008-10-24 07:52 am (UTC)Безусловно, есть области, где это не так, но ими дело далеко не ограничивается.
(no subject)
Date: 2008-10-24 08:10 am (UTC)Re: главная задача тестирования
Date: 2008-10-23 08:42 am (UTC)(no subject)
Date: 2008-10-23 06:55 pm (UTC)обратите внимание на два неотъемлемых свойства этой задачи: "продакшн" и "правильно". мало пользы, если правильно работает один код, а в продакшн идёт другой.
(no subject)
Date: 2008-10-24 07:30 am (UTC)(no subject)
Date: 2008-10-24 08:07 am (UTC)(no subject)
Date: 2008-10-24 09:05 am (UTC)(no subject)
Date: 2008-10-25 07:52 pm (UTC)(no subject)
Date: 2008-10-24 07:35 am (UTC)Гм-гм. Ну так если в этих местах разрабатывают правильно работающий код, то зачем там ещё какое-то тестирование? :)~
(no subject)
Date: 2008-10-21 03:52 pm (UTC)Ассерт в данном тексте рекомендуется как вариант техники спецификации предусловий-постусловий. В этом качестве он, во-первых, позволяет показать вам предусловие или инвариант синтаксически, так, что читатель сразу видит, что это никак к штатному режиму работы не относится. Во-вторых, у программиста его срабатывание не раскручивает стек, а сразу поднимает в правильном месте дебаггер, с полным доступом к состоянию. В третьих, это можно убрать из релиза, что важно, потому как проверка постусловий и инвариантов может быть довольно накладна.
Пример - функция сортировки. Постусловие для сортировки проверяется пробежкой по массиву и проверкой x[i] >= x[i-1], что вовсе не хочется тянуть в релиз.
Ну и, наконец, в четвертых, макрос типа ASSERT можно реализовать как угодно, в том числе и с пробросом exception. Можно завести их несколько, разных типов, или один, который будет вести себя по разному в дебаге и релизе. Это уже не принципиально.
(no subject)
Date: 2008-10-21 06:28 pm (UTC)с дебаггером всё то же самое; если мы бежим под дебаггером, то попадём туда, где exception поднялся, а не где он поймался. если мы не бежим под дебаггером, то ассёрт не поможет так же, как и exception.
осталось использование ассёрта для предусловия. в этом случае exception очевидно лучше, потому что во-первых, реализует известный грамотный паттерн, а во-вторых, потому что ведёт себя одинаково как в дебажном, так и в релизном билде. я думаю, мне не надо рассказывать, как трудно ловить баги, которые происходят только в релизном билде.
(no subject)
Date: 2008-10-22 11:53 am (UTC)Разница в том, что предусловия-постусловия являются предположениями программиста относительно состояния системы, которое, как ему кажется, не может не выполнятся в указанной строке кода ни при каких условиях. Выраженны эти предположения в явном виде. И от тестового набора совершенно не зависят. Они помогают локализовать проблему при любом тестовом покрытии, которое может генерироваться как юнит-тестами, так и системными тестами.
Предусловия-постусловия - это часть спецификации программы, и должны быть отделены в тексте явно, синтаксически от гражданского, "функционального" кода, что сильно помогает читателю кода. Уже это - необходимость. Второе - в силу того, что они могут существенно замедлять работу в ряде случаев, как я показал в примере, возможность вырезать их флагом компиляции не помешает.
Это ведет к тому, что инварианты и пред-пост условия лучше проверять макросами. Как эти макросы будут устроены внутри - бросать исключение, или еще как - уже не так принципиально. Скажем, у нас был довольно тонкий контроль над инвариантами, позволяющий включать и отключать их при компиляции на уровне модулей. Именно потому, что их проверка зачастую тормозная, и нафиг не нужно иметь их включенными везде "на всякий случай".
Насчет релизного билда - те баги которые остаются только в нем, достаточно редки, и для их поимки никто не мешает сделать специальную сборку с включенными инвариантами-постусловиями. Особенно, если есть возможность помодульного их включения. Хотя, по опыту - баги специфичные для релиза обычно инвариантами-постусловиями не ловятся.
(no subject)
Date: 2008-10-22 09:40 pm (UTC)спецификация находится в вордовском документе. ассёрт - это проверка, а не спецификация. и юнит-тест - тоже проверка. я не вижу никакой разницы между ассёртом на постусловие и юнит-тестом. объясните, пожалуйста.
> Разница в том, что предусловия-постусловия являются предположениями программиста относительно состояния системы, которое, как ему кажется, не может не выполнятся в указанной строке кода ни при каких условиях.
я не понимаю, что это значит. кажется, м-да. писать assert(myArgument != null) можно или нельзя? если мне кажется, что моя функция не будет вызываться с null аргументом?
> Они помогают локализовать проблему при любом тестовом покрытии
разумеется, нет. они помогут локализовать проблему только тогда, когда тесты, прогоняемые в test environment, приведут к нарушению инварианта. то есть, помогут или нет - полностью зависит от тестового покрытия. а в production environment как раз ассёртов нет, так что там нам ассёрт не поможет (а exception поможет).
> Как эти макросы будут устроены внутри - бросать исключение, или еще как - уже не так принципиально.
это абсолютно принципиально. потому что главное отличие ассёрта на предусловие от проверки его с выбросом ексепшена в том, что второе в продакшн коде остаётся, а первое - нет. если ассёрт на предусловие сгенерирован макросом, который оставляет выброс ексепшена в продакшн коде, то разница между ними полностью исчезает, и эксепшн в явном виде тогда всё равно лучше ввиду стандартности.
> никто не мешает сделать специальную сборку с включенными инвариантами-постусловиями.
сюрреализм какой-то. что это значит-то, "не мешает"? мы или оставляем, или нет. оба варианта я рассмотрел, в обоих случаях в ассёрте нет смысла.
(no subject)
Date: 2008-10-23 01:14 am (UTC)Неотъемлемым признаком любого теста, и его характеристикой, является величина тестового покрытия. Если вы написали нечто, и покрытие в результате его выполнения - ноль, то это тестом не является по всем понятиям.
Вот у вас есть код, скажем, вычисления квадратного корня. Вы добавляете в его начало ASSERT( X >= 0 ), а в конец - ASSERT( Y * Y - X < Epsilon ).
Вопрос - у вас хоть как-то увеличилось тестовое покрытие? Нет. Было ноль, остальсь ноль. Следовательно, это не тест. Ни юнит, ни какой другой.
Но. Эта пара предусловие-постусловие - это формальная спецификация вычисления квадратного корня, через которую ничего кроме квадратного корня не пройдет, независимо от алгоритма вычисления. Находится она в документе или коде - не важно. Хорошо, что она находится в коде - она проверяется в тот момент, когда вы таки напишете юнит тест, и он даст вам нагрузку на функцию квадратного корня, обеспечив тестовое покрытие. А от того, что она будет в документе - от нее ноль толку.
(no subject)
Date: 2008-10-23 07:02 pm (UTC)(no subject)
Date: 2008-10-23 09:14 pm (UTC)Первый из них состоит в том, что предусловия не нужны, и что функция должна отрабатывать определенным в спецификации образом при _любых_ значениях аргументов. В случае, если они некоректны - надо бросить исключение. При данном подходе формальная спецификация расписывается в виде таблицы, где в первой колонке стоит условие на аргументы, а в правой колонке - постусловие. Для квадратного корня будет что-то вроде
x >= 0 && Epsilon > 0 | y^2 - x | < Epsilon
x < 0 throw InvalidArgument( "Х" )
Epsilon <=0 throw InvalidArgument( "Epsiilon" ).
При данном подходе вы можете проверить спецификацию на полноту, убедившись, что условия в первой колонке накрывают все варианты.
Это ваш подход, он вам понравится.
Второй подход состоит в том, что поведение функции определено только при некотором предусловии. А что произойдет в остальных случаях - нас не волнует, поведение функции не определено. То есть, в корректно написанной программе предусловие выполняется всегда, и вы на это закладываетесь. В данном случае можно ставить ASSERT.
В сущности, разница между данными подходами философская, на практике проявляющаяся в бросании или не бросании исключения :). Я бы в любом случае предложил завернуть предусловие в макрос - для читабельности, чтобы подчеркнуть что это предусловие, и реализовать его внутри как нравится - пробросом исключения в релизе и выпадением в дебаггер в дебаге, или еще как. Или, скажем, ваще записью в лог для режима, когда юнит-тесты гоняются.
(no subject)
Date: 2008-10-23 09:57 pm (UTC)Она в том, что при первом подходе у нас функция не должна иметь разрушительных последствий ни в коем случае. Не можем же мы в спецификации прописать, скажем - а при таких условиях мы вылезаем за границы массива и начинаем тереть то, что рядом данными из буфера х :). При этом, эксцепшн не обязательно в случае ошибки кидать - можно скажем error code вернуть. Исключения не везде есть вообще, если говорить, скажем, о той же микроволновке и ее микроконтроллере.
Во втором же подходе, если предусловие не выполнилось, то может произойти что угодно, и битье по памяти, и segmentation fault. Или просто испорченное некорректное глобальное состояние. Об этом можно просто не думать. Типа.
Есть мнение, что первый подход в целом лучше. Особенно в случае "опасных" языков, вроде С и С++. Типа защитное программирование и все такое - когда надо уметь работать в условиях присутствия программных ошибок.
А вот в Эрланге рулит второй подход - язык специально устроен так, что можно следовать ему и программить только хороший случай. Говорят, в высоконадежных системах до четверти объема кода - защитного.
(no subject)
Date: 2008-10-23 10:51 pm (UTC)а что в эрланге сделает функция вычисления sqrt от отрицательного числа?
(no subject)
Date: 2008-10-23 11:36 pm (UTC)Получается похоже именно так, и даже если их в языке нет, то все равно предусловия надо проверять в релизе, и сигнализировать об их нарушении как-нибудь аккуратно. Кодами возврата, например.
> а что в эрланге сделает функция вычисления sqrt от отрицательного числа?
либо исключение бросит, либо, что для квадратного корня в случае эрланга более предпочтительно, вернет ошибку. Язык-то динамический, можно что угодно возвращать. А вот если результат-ошибка не сматчится (то есть, если вызывающий код не ожидает получить ошибку, а ждет именно числа - то есть рассчитывает на good case), то все равно вылетит исключение. Которое, будучи не пойманным, завалит процесс, в котором это работает (штатная ситуация), о чем узнают связанные с ним процессы, и предпримут необходимые меры.
Пример - как можно открывать файл. Как-то вот так:
{ ok, File } = file:open( "name" ).
ok - это константа в данном случае, и если первый элемент тупла-результата вызова open будет не ok, а например, error - то вылетит исключение bad match.
Учитывая, что процессы в эрланге применяются примерно так же, как объекты в С# (их могут быть сотни тысяч в системе легко, и для полноты картины представьте, что каждый из них обрабатывает сообщения асинхронно на собственном треде), и полностью изолированны друг от друга, это создает интересные возможности.
(no subject)
Date: 2008-10-23 11:51 pm (UTC)sqrt( X, Epsilon ) when X >= 0, Epsilon > 0 -> ...типа реализация
Все, этого достаточно. Если вызовут с неправильными аргументами, опять вылетит bad match. А можно написать так:
sqrt( X, Epsilon ) when X >= 0, Epsilon > 0 -> ...типа реализация
sqrt( _, _ ) -> error.
Тогда вместо вылета bad match будет возвращен error.
(no subject)
Date: 2008-10-24 12:01 am (UTC)(no subject)
Date: 2008-11-17 12:50 pm (UTC)Чтобы вернуть тупл - достаточно написать { expr1, expr2, ..., exprN }. И все - тип данного выражения - тупл. Кажется в F# это должно выглядеть так: ( expr1, expr2, ..., exprN ).
Вообще - синтаксически формирование структуры и ее разбор на компоненты (матчинг) выглядят одинаково, вся разница в том, с какой стороны от присваивания стоит выражение. Это касается не только туплов, но и как минимум списков (которые в квадратных скобках).
(no subject)
Date: 2008-11-18 07:29 am (UTC)(no subject)
Date: 2008-11-18 01:21 pm (UTC)и вот еще чего вспомнилось
Date: 2008-10-23 10:22 pm (UTC)Если вы ей не пользуетесь, то для локализации проблем вы вынуждены применять юнит-тесты. А они, во-первых, большие, во-вторых, откидываются при рефакторинге и изменениях интерфейсов, в третьих - тестируют зачастую код в том числе и в тех режимах, в которые он никогда не попадет.
Однако, если код нашпигован пред и постусловиями, то вы и на общесистемном тесте локализуете ошибку без проблем. Да и не только на автоматическом тесте - в любой ситуации. Что позволяет сконцентрирваться на более крупных прикладных тестах, которые более устойчивы к рефакторингам, меньше по совокупному объему, и тестируют только нужные режимы. Сами постусловия также на порядок более устойчивы к рефакторингам, чем юнит-тесты, как нетрудно заметить, и кроме того, повышают "понимабельность" чужого кода.
Итого, вы имеете чистый выигрышь по продуктивности, если используете технику "исполняемых спецификаций", назовем ее так. Этот подход рвет test-driven development и модные нынче юнит-тесты на кровавые ошметки. А в случае асинхронной системы, которая построена на асинхронных событиях (это был как раз мой случай), с недетерминированной последовательностью событий, отрыв от TDD в продуктивности становится уже не количественным, а качественным. Применяя инварианты, пред и постусловия, я могу в крайне сжатые сроки отладить то, что юнит-тестами за разумное время отладить просто нельзя.
Короче, формальные спецификации рулят со страшной силой. Нужен для них только ASSERT (хотя я, помнится, написал специальные макросы) да мозг в голове, чтобы правильно им пользоваться. Это наглядный пример, когда предварительное проектирование, элементом которого безусловно является данная техника, убедительно кладет на лопатки экстремальные пионерские подходы "с шашкой наголо" - такие как XP.
(no subject)
Date: 2008-10-23 10:47 pm (UTC)да, но ведь бросание эксепшена вместо ассёрта точно так же локализует ошибку.
(no subject)
Date: 2008-10-23 11:13 pm (UTC)Для предусловий, допустим, согласен - это совсем нехорошо, когда они не выполняются, это всегда фатальная ошибка, можно бросить исключение. Однако, принципиально то, что я свои _постусловия_ не хочу тащить в релиз. Они у меня бывают достаточно тяжеловесны, и могут иметь O(N) в асимптотике, или хуже. Тормозят они, их даже из многих дебагов надо резать, не то что из релиза. Например, какая-нибудь пробежка по контейнеру с проверкой предиката на каждом элементе перед выходом из функции. Или рекурсивная проверка какого-либо свойства. Кроме того, несоблюдение постусловия - это часто не фатальная ошибка, так же как и несоблюдение некоторых инвариантов в теле кода. Ну пусть нарисует мне что-нибудь немного не так, как надо, зачем исключениями-то людей пугать, и устраивать им из-за всякой ерунды feature failure и отказ сервиса? :) Авось не заметят? А? Раньше как-то жили без сообщений "извините, все в целом хорошо, но программа где-то внутри работает немного неправильно - корень квадратный вычислился с ошибкой, и поэтому операция полностью отменена на всякий случай. Попробуйте еще раз"? :) Интересно, кстати, как к такому техподдержка отнесется. :) Или как вы предлагаете реагировать на исключение в релизе о невыполнении постусловия? :)
Да вы сами поймете, когда попробуете хороших, содержательных постусловий для своего кода пописать. Попробуйте покидать из постусловий исключения, напишите, что получится. Напишите - напрасно пугает Гапертон, все у меня отлично, я доволен. :) Хотя, я думаю, как только ваши постусловия станут сложны, вы начнете жалеть релизный перформанс, даже если и правда все остальное будет хорошо.
(no subject)
Date: 2008-10-24 12:01 am (UTC)ну да, про постусловия я уже согласился.
(no subject)
Date: 2008-10-24 02:55 am (UTC)(no subject)
Date: 2008-10-23 10:35 pm (UTC)Нужно. Следуя данной технике, надо _все_ ваши предположения, на которые вы в своей программе полагаетесь, фиксировать явно в виде assert-ов. Это, кстати, поможет читать, понимать, и рефакторить ваш код - людям не придется догадываться, на что вы полагаетесь.
(no subject)
Date: 2008-10-22 10:24 pm (UTC)(no subject)
Date: 2008-10-23 10:28 pm (UTC)(no subject)
Date: 2008-10-23 10:39 pm (UTC)(no subject)
Date: 2008-10-21 06:31 pm (UTC)(no subject)
Date: 2008-10-21 05:44 pm (UTC)(кроме шуток, в msdn на полном серьёзе советуют дефайнить ассерты так, чтобы они в релизе вот в это вот превращались. Даже не знаю, это WTF или наоборот глубокая мудрость)
(no subject)
Date: 2008-10-21 06:40 pm (UTC)