Здравствуйте, konsoletyper, Вы писали:
K>>> И тут уже вопрос не только о системе типов языка. Например, в том же C# есть нехорошая в этом смысле конструкция as, которую стараются избегать. Готов обсудить конкретные особенности конкретных языков, которые помогают или мешают легче обнаруживать ошибки. N>>А что в ней не так? K>В ней — это в конструкции as из C# или в чём-то другом. На всякий случай про C# (хотя это уже везде, где можно, обсуждали).
Моё знание C# недалеко ушло от helloworld.
N>>Да, javascript — кошмарный ужас, причём не только из-за таких примеров. Тут в соседней ветке обсуждалось. Когда "5" + 3 == 53, но "5" — 3 == 2, авторов языка следует изгонять на Лабрадор. В нормальном языке неявные конверсии типов, такие, как между строкой и числом, должны быть запрещены. K>А вот это как раз не страшно. Страшно не неочевидное поведение языка — можно просто избегать конструкций, которые потенциально приводят к подобным ужасам.
Их тут невозможно избежать. Достаточно получить в аргументах или в пришедшем с сети неожиданный тип данных, и проблема будет замаскирована.
K> Страшно, когда есть фича, без которой нельзя обойтись (например, локальные переменные), и которая содержит в себе бомбу замедленного действия.
Опечатки в локальных переменных ловятся на одном первом проходе теста, неважно, ручного или
автоматизированного.
K>По поводу конверсии. Конверсии число -> строка вполне допустимы.
Нет. Во-первых, они дают такие вот неожиданные эффекты. Во-вторых, число может быть представлено по-разному в разных случаях, и автоматическая конверсия этого не учитывает.
K> Ошибки, которые возникают из-за конверсии, вполне отлавливаются системой типов.
В Javascript? Прц.
N>>Но не только с ним тут грабли. Я сейчас перечитываю "Рефакторинг" Фаулера. Там в начале есть пример ошибки в коде на Java, выловленной только тестом, от неявной конверсии double -> int. K>Наверное, из double в int. А можно пример?
объявление thisAmount должно было быть double вместо int.
И даже warning'а не выдало, насколько я понял автора, хотя в нормальном языке вообще не должно было скомпилироваться, ибо преобразование из плавучего типа в целый должно быть только явным с указанием необходимого действия (round, trunc, floor или ceil, и тех round'ов может быть несколько).
K>>> Этот undefined может очень долго путешествовать по графу объектов, пока, например, кто-нибудь не попытается вызвать у него метод. В Ruby есть похожая штука, хорошо хоть в случае с локальными переменными это побороли. N>>Жуть. K>Да-да, это-то как раз и есть жуть, а не какая-нибудь штука вроде положительного и отрицательного нулей или проблем с бесконечностями и NaN.
А при чём тут они? Это специфика предметной области (плавучка сама по себе уже предметная область, хоть и промежуточная), а не тараканы языка.
Здравствуйте, netch80, Вы писали:
N>>>Да, javascript — кошмарный ужас, причём не только из-за таких примеров. Тут в соседней ветке обсуждалось. Когда "5" + 3 == 53, но "5" — 3 == 2, авторов языка следует изгонять на Лабрадор. В нормальном языке неявные конверсии типов, такие, как между строкой и числом, должны быть запрещены. K>>А вот это как раз не страшно. Страшно не неочевидное поведение языка — можно просто избегать конструкций, которые потенциально приводят к подобным ужасам.
N>Их тут невозможно избежать. Достаточно получить в аргументах или в пришедшем с сети неожиданный тип данных, и проблема будет замаскирована.
Вот-вот. А в Java, например, если это какой-нибудь JAXB, то все "неожиданные типы данных" отловятся автоматом, без лишних проверок в коде (которые вручную можно и забыть проставить).
N>Опечатки в локальных переменных ловятся на одном первом проходе теста, неважно, ручного или N>автоматизированного.
Кстати, а для динамически типизированных языков бывают code coverage tools? Или проверка того факта, что весь код покрыт тестами, есть только на честном слове?
K>>По поводу конверсии. Конверсии число -> строка вполне допустимы.
N>Нет. Во-первых, они дают такие вот неожиданные эффекты. Во-вторых, число может быть представлено по-разному в разных случаях, и автоматическая конверсия этого не учитывает.
Ну так оно и ясно, что компилятор не телепат и заранее не знает, в каком формате выводить числа. А вот, например, для логгирования это очень удобно. И эффект очень даже предсказуемый: сложили число со строкой, получили строку, ибо неявное преобразование возможно только из числа в строку. Но это только в нормальных языках, где обратного неявного преобразования не существует. А в JavaScript, да, всё очень неожиданно.
K>> Ошибки, которые возникают из-за конверсии, вполне отлавливаются системой типов.
N>В Javascript? Прц.
Так я и высказался по поводу системы типов в контексте защиты статической типизации. В Javascript её нет, потому получаются вот такие вот внезапности. А была бы статическая типизация — проблем не было бы.
N>Основные строки из примера в книге:
N> int thisAmount = 0; N> ... N> thisAmount += (each_getDaysRented() — 2) * 1.5;
N>объявление thisAmount должно было быть double вместо int. N>И даже warning'а не выдало, насколько я понял автора, хотя в нормальном языке вообще не должно было скомпилироваться, ибо преобразование из плавучего типа в целый должно быть только явным с указанием необходимого действия (round, trunc, floor или ceil, и тех round'ов может быть несколько).
Не верю!
K>>Да-да, это-то как раз и есть жуть, а не какая-нибудь штука вроде положительного и отрицательного нулей или проблем с бесконечностями и NaN.
N>А при чём тут они? Это специфика предметной области (плавучка сама по себе уже предметная область, хоть и промежуточная), а не тараканы языка.
А при том, что ругатели JavaScript часто ругают эти моменты, не обращая внимания на большую беду с локальными/глобальными переменными и undefined'ами.
Здравствуйте, DarkGray, Вы писали:
U>>Лучше тот язык, который позволяет записать решение меньшим количеством сущностей. Биты здесь вообще не в тему.
DG>есть два количества сущностей: DG>количество сущностей в алфавите (в наборе понимаемых сущностей),
Это не сущности, а количество синтаксических конструкций в языке. Речь не о них.
DG>количество символов(сущностей) в записи решения.
Это тоже не то.
Первичным является количество логических сущностей, т.е. то минимально возможное количество сущностей, которого достаточно для решения задачи без привязки к языку. Язык должен позволять записать решение близким образом к логическому решению. Если такая запись возможна и соответственно количество сущностей в программе близко к количеству логических сущностей, то это хороший язык. Если же для записи логического решения язык потребовал ввести множество дополнительных сущностей, то это плохой язык.
Естественно когда речь идет о минимуме количества сущностей, то не нужно понимать это как примитивное определение количества. Сущности разного рода могут иметь кардинально отличающийся вес, например, сеттер для объекта с большим временем жизни может быть много дороже десятка геттеров. Соответственно понятие количества включает в себя веса сущностей.
DG>ты сейчас о каком из них? DG>если минимизировать первое — то лучшим ЯП становится: машина тьюринга, там всего две сущности в алфавите: 0 и 1
Машина Тьюринга это очень плохой язык, т.к. для записи логического решения требуется добавить в программу огромное количество дополнительных сущностей.
DG>если минимизировать второе — то лучшим ЯП становится: ЯП с бесконечным алфавитом, когда все программы записываются одним символом из этого алфавита.
Здесь получается одна сущность, но с огромным весом, соответственно вместо упрощения получается усложнение.
U>Естественно когда речь идет о минимуме количества сущностей, то не нужно понимать это как примитивное определение количества. Сущности разного рода могут иметь кардинально отличающийся вес, например, сеттер для объекта с большим временем жизни может быть много дороже десятка геттеров. Соответственно понятие количества включает в себя веса сущностей.
Т.е. количество сущностей измеряется не в штуках, а в неких единицах сложности. Аналогично тому как, к примеру, количество капитала измеряется не в штуках заводов, домов и тапочек, а в деньгах.
U> Первичным является количество логических сущностей,
да, я в целом понимаю о чем ты, и я с этим согласен.
вопросы дальше уже с целью поковыряться в деталях, и попробовать еще улучшить данный постулат.
1. Должны ли сущности записи совпадать с сущностями логики решения?
2. Должны ли сущности исполнения совпадать с сущностями логики решения?
3. Что такое вес логической сущности? И как его можно измерить или вычислить?
4. Можно ли выделить какие-то базовые сущности? Что это за сущности?
Все вменяемые тестовые системы для того же питона позволяют запускать и проверку покрытия
параллельно с тестами. Тоже и IDE'шки http://pydev.org/manual_adv_coverage.html
Здравствуйте, DarkGray, Вы писали:
DG>1. Должны ли сущности записи совпадать с сущностями логики решения?
Разумеется. Иначе нам придется делать двойную работу. Вначале придумывать решение в голове, а затем неочевидным образом транслировать его в код.
DG>2. Должны ли сущности исполнения совпадать с сущностями логики решения?
Нужно для удобства отладки.
DG>3. Что такое вес логической сущности? И как его можно измерить или вычислить?
Если подумать, то вовсе не факт, что вес имеет смысл учитывать. Т.к. если смотреть в корень проблемы, то возможно важны как раз штуки, а не веса. Например, почему очень дороги сеттеры? Потому что при традиционном методе программирования информация в программе дублируется множество раз. Возьмем самый примитивный пример. Есть бизнес-объект, его нужно показать пользователю. Мы берем переменную этого объекта и копируем ее, к примеру, в textBox. Т.е. на ровном месте получаем дублирование информации. Соответственно дальше изменение состояние бизнес-объекта (сеттер) должно порождать множество изменений (сеттеров) объектов от него зависящих. А т.к. отслеживание зависимостей это не тривиальная задача, то сложность программы растет лавинообразно, и возникает впечатление, что ничего страшнее сеттеров нет. Хотя на самом деле корень проблемы не в сеттерах, а в дублировании состояния.
Если же мы с помощью автоматических кэшей и LazyMaker'ов (тот же автоматический кэш, но не пересчитывающий результат, а выполняющий определенное действие при изменении входов) от дублирования состояния избавляемся, то сеттеры становятся ничуть не дороже геттеров. Например, если в предыдущем примере, мы textBox опишем в виде LazyMaker'а зависящего от переменной бизнес-объекта и вызов этого LazyMaker'а поместим в onPaint формы, то после изменения состояния объекта нам будет достаточно принудительно перерисовать форму (или мир в общем случае), чтобы гарантировать корректное состояние. Соответственно изменение состояния становится тривиальной операцией, не вносящей дополнительной сложности.
DG>4. Можно ли выделить какие-то базовые сущности? Что это за сущности?
По привычке я считал, что главное зло это сеттеры и лишние слои абстракции. Но в свете предыдущего пункта сеттеры оказались реабилитированными, а истинным виновником проблемы оказалось дублирование состояния. По поводу лишних слоев абстракции надо подумать, возможно они тоже плохи не сами по себе, а из-за того что нарушают нечто более фундаментальное. Так вроде да, получается, что и лишние слои абстракций плохи тем, что на ровном создают лишнее состояние с неизбежным дублированием состояния логического. Т.е. похоже, что от принципа минимума состояния и нужно плясать.
DG>>1. Должны ли сущности записи совпадать с сущностями логики решения?
U>Разумеется. Иначе нам придется делать двойную работу. Вначале придумывать решение в голове, а затем неочевидным образом транслировать его в код.
а наоборот? могут ли сущности записи решения не совпадать с сущностями логики решения и исполнения? и зачем такое бывает необходимо?
DG>>2. Должны ли сущности исполнения совпадать с сущностями логики решения?
U>Нужно для удобства отладки.
а для эффективности исполнения?
DG>>3. Что такое вес логической сущности? И как его можно измерить или вычислить?
U> Потому что при традиционном методе программирования информация в программе дублируется множество раз. ... в дублировании состояния.
дублирование состояния в любом случае возникает внутри кэшей, резервировании, при использовании распределенной системы и т.д.
U>Если же мы с помощью автоматических кэшей и LazyMaker'ов .. U> Соответственно изменение состояния становится тривиальной операцией, не вносящей дополнительной сложности.
при большом кол-ве "выходов"(text-box-ы, экраны, подписки других систем и т.д.) появляется задача отслеживания, какие выходы необходимо пересчитать на основе текущих изменений, и эта задачи не тривиальная
DG>>4. Можно ли выделить какие-то базовые сущности? Что это за сущности?
U>По привычке я считал, что главное зло это сеттеры и лишние слои абстракции. Но в свете предыдущего пункта сеттеры оказались реабилитированными, а истинным виновником проблемы оказалось дублирование состояния. По поводу лишних слоев абстракции надо подумать, возможно они тоже плохи не сами по себе, а из-за того что нарушают нечто более фундаментальное. Так вроде да, получается, что и лишние слои абстракций плохи тем, что на ровном создают лишнее состояние с неизбежным дублированием состояния логического. Т.е. похоже, что от принципа минимума состояния и нужно плясать.
получается ты сейчас говоришь про объем состояния, которым приходится оперировать (а не которое хранится), т.к. абстракции обычно это просто отображение из одной формы в другую, и редко добавляют увеличение хранимого состояния.
и при оперировании информацией абстракции часто решают задачи уменьшения кол-ва информации, которым приходится оперировать в ряде конкретных задач.
как это измерять?
Здравствуйте, DarkGray, Вы писали:
DG>а наоборот? могут ли сущности записи решения не совпадать с сущностями логики решения и исполнения? и зачем такое бывает необходимо?
Это может быть оправдано в целях оптимизации, при условии, что оптимизация нам важнее удобства программиста.
DG>>>2. Должны ли сущности исполнения совпадать с сущностями логики решения? U>>Нужно для удобства отладки. DG>а для эффективности исполнения?
Аналогично первому пункту.
DG>дублирование состояния в любом случае возникает внутри кэшей, резервировании, при использовании распределенной системы и т.д.
Автоматический кэш не дублирует состояние, т.к. внешним образом невозможно добиться, чтобы он начал возвращать неверное состояние.
DG>при большом кол-ве "выходов"(text-box-ы, экраны, подписки других систем и т.д.) появляется задача отслеживания, какие выходы необходимо пересчитать на основе текущих изменений, и эта задачи не тривиальная
Если выстраивать зависимости как цепочки автоматических кэшей дополняемые LazyMaker'ами в тех случаях, когда произошедшее изменение нужно отобразить немедленно, то эта тривиальная задача.
DG>получается ты сейчас говоришь про объем состояния, которым приходится оперировать (а не которое хранится), т.к. абстракции обычно это просто отображение из одной формы в другую, и редко добавляют увеличение хранимого состояния.
Зло и то, и то. Т.е. без необходимости нельзя увеличивать ни количество хранимого состояния, ни количество разновидностей сущностей, которыми нужно оперировать.
DG>и при оперировании информацией абстракции часто решают задачи уменьшения кол-ва информации, которым приходится оперировать в ряде конкретных задач. DG>как это измерять?
Первый вид сложности — это количество сущностей (как хранимых, так и разновидностей).
Второй вид сложности — это количество степеней свободы сущности. Например, readonly объект имеет меньше степеней свободы, чем модифицируемый объект, и соответственно проще.
В некоторых случаях снижение сложности второго рода приводит к увеличению сложности первого рода. Однозначного ответа когда это оправдано нет, надо рассматривать конкретные [группы] случаев.