Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.
Соответственно, для каждой операции сравнения разный набор этих вариантов превращается в true или false, там ниже таблицы истинности.
Как это работает — понятно, а что это с точки зрения типов и как это думать — перестал понимать совсем.
Maybe Float всем лучше! Обнаружил тут крейт Noisy Float, не такой уж непопулярный, который как раз-таки с безопасным float
UPD: безопасный он довольно условно: просто паникует при любом ненормальном числе. Видимо, это поведение более востребовано, чем аккуратный Option вокруг почти всего
Хороший вопрос! Для меня сравнение тут возможно трёх видов (Number во всех трёх случаях включает не NaN, Option<Number> — включает):
Option<Number> -> Option<Number> -> Option<Bool>, когда сравниваются только числа, а не-числа приводят к принципиально другому результату. Это как в SQL с nullable-типами: NULL = NULL даёт NULL
Option<Number> -> Option<Number> -> Bool по типу SQL-ного IS NOT DISTINCT FROM (оно же <=> в MySQL). Это Ваш вариант
Option<Number> -> Option<Number> -> Bool , в виде модификации первого варианта, когда Some(true) приравнивается к true, а всё остальное — к false. Это вариант из IEEE 754. Кстати, преобразование Some<Bool> в Bool здесь по аналогии с работой WHERE в SQL: отбираются только строки со значением условия TRUE, а FALSE и NULL — нет, происходит эдакое впихивание трёх значенийOption<Bool> в два значения Bool.
Я думаю о том, что блок с условием NaN == NaN не выполнится, по аналогии с тем, как в SQL не отберутся строки с NULL = NULL.
Кстати, читал тут документацию, и почему-то NaN ** 0 в Javascript даёт единицу. Вероятно, потому что нулевая степень чего угодно, даже жаренной с грибами картошки, даёт всё равно единицу.
Был бы явный Option, было бы логично: (Some(5) == Some(5)) == Some(true), при этом (Some(5) == None) == None) и (None == None) = None).
Но Option тут половинчатый, и такое решение выглядит довольно логично. В конце концов, NaN представляет разные ситуации не-чисел, например, parseInt("blah") и "".charCodeAt(1). Было бы странно говорить, что они равны в контексте сравнения чисел.
Много и по делу, спасибо! Если бы хабрамаркетологи следовали хотя бы части упомянутых рекомендаций...
Одно только резануло: милый крабик Rust он, конечно, симпатичный, но гораздо больший эмоциональный отклик у меня вызывает красота концепций, положенных в основу языка. И без крабика я точно так же бы его любил.
А вот не менее милый суслик Go меня скорее раздражает, потому что мне не нравится дизайн языка (и, получается, поэтому даже хороший маркетинг для меня не работает, как это и описано в статье).
UPD: комментарий не предназначен для разжигания. Я просто хочу отметить, что для создания эмоциональной связи с продуктом иногда достаточно и просто кусков кода
Да, согласен, просто хотелось отметить, что Ваша интерактивная визуализация обладает такой же «магией наглядности», превращающей не самые простые выкладки в нечто совершенно очевидное.
Интерактивное демо просто восхитительное! — сразу всё становится понятно. И ещё вспомнилось видео про спирали из простых чисел, не оно ли послужило источником вдохновения?
другой программист не знает, какие из всех возможных поведений вы использовали намеренно, а про какие не подумали
Кажется, этот аргумент применим вообще к любому мало-мальски нетривиальному коду.
У нас же разговор про запрос, а не про схему данных.
Задачу я сформулировал следующим образом (и как бы мне теоретически мог сформулировать её коллега-маркетолог): "Интересно, кто что себе покупает на ДР", и условие в запросе в точности соответствует постановке.
Для меня это очевидно не из-за того, что я это писал, а благодаря тому, что я давно привык к подобному поведению SQL и ожидаю именно его. Это не побочный эффект и не баг, это часть стандарта.
Что касается моделирования задачи, то условие «у некоторых пользователей есть дата рождения» полностью соответствует nullable-колонке в таблице с пользователями, разве нет?
А как по-вашему 1 > NULL — это истина или ложь? А 1 < NULL?
Если я хочу сравнить с числом средний чек пользователя, а у него покупок нет, то AVG(amount) будет NULL, даже если вообще вся база у меня спроектирована без нулевых колонок.
Допустим, табличка с пользователями, у некоторых есть дата рождения, и табличка с заказами, в которых есть дата оплаты, но только в том случае, если он оплачен.
Интересно, кто что себе покупает на ДР.
Типа: ON user.id = order.user_id AND user.bdate = DATE(order.paid_at).
В реальности я бы скорее смотрел заказы за неделю до дня рождения, т. е. BETWEEN bd - INTERVAL 7 DAY AND bd, и всё бы тоже прекрасно работало.
Вам ни разу не приходилось делать JOIN по nullable-колонкам? Если ко всем NULLам из одной таблицы приджойнятся все NULLы из другой, будет хорошо и правильно?
Почему Вы упорно пытаетесь свести NOT DISTINCT к равенству/идентичности? Это просто отдельный концепт.
Если уж говорить об аналогиях NULL среди типов данных языков программирования, то для меня это всегда казалось чем-то близким монаде MayBe или типу Option .
А поскольку NULL может случиться где угодно, в Option получаются неявно завёрнуты вообще все значения.
И distinct/равенство, отлично вписываются в эту концепцию: distinct сравнивает сами Option'ы (и возвращает bool), а равенство — их содержимое (и возвращает тоже Option). А вот в JS ничего подобного нет.
В одном из ответов на stackoverflow приведены интересные цитаты из спецификации SQL. Если кратко: NULL друг другу не равны (not equal), но при этом они неразличимы (not distinct). Таким образом, GROUP BY не попирает догму, а просто использует иную концепцию.
Собственно, SQL2003 определяет оператор IS DISTINCT FROM, а в MySQL есть <=>
Если NULL будет равен сам себе, то внешние соединения по нулевым полям дадут совершенно неинтуитивный результат. Отсутствующее отчество у Джона Смита — это не то же, что отсутствующее отчество у Джимми Вонга
Вот только 0,001 секунд без fwirte достигается из-за того, что умный компилятор вырезает вообще все вычисления, потому что их результат никак не используется.
~==~
выглядит ровно как сравнение в SQL с учётом nullable, на этот счёт недавно была полемическая статья.Я совершенно согласен, что в целом для
Maybe
обыкновенный==
выглядит удобнее почти во всех случаях.Кстати, в IEEE 754 прочитал (раздел 5.11):
Соответственно, для каждой операции сравнения разный набор этих вариантов превращается в
true
илиfalse
, там ниже таблицы истинности.Как это работает — понятно, а что это с точки зрения типов и как это думать — перестал понимать совсем.
Maybe Float
всем лучше! Обнаружил тут крейт Noisy Float, не такой уж непопулярный, который как раз-таки с безопаснымfloat
UPD: безопасный он довольно условно: просто паникует при любом ненормальном числе. Видимо, это поведение более востребовано, чем аккуратный
Option
вокруг почти всегоХороший вопрос! Для меня сравнение тут возможно трёх видов (
Number
во всех трёх случаях включает неNaN
,Option<Number>
— включает):Option<Number> -> Option<Number> -> Option<Bool>
, когда сравниваются только числа, а не-числа приводят к принципиально другому результату. Это как в SQL с nullable-типами:NULL = NULL
даётNULL
Option<Number> -> Option<Number> -> Bool
по типу SQL-ногоIS NOT DISTINCT FROM
(оно же<=>
в MySQL). Это Ваш вариантOption<Number> -> Option<Number> -> Bool
, в виде модификации первого варианта, когдаSome(true)
приравнивается кtrue
, а всё остальное — кfalse
. Это вариант из IEEE 754. Кстати, преобразованиеSome<Bool>
вBool
здесь по аналогии с работойWHERE
в SQL: отбираются только строки со значением условияTRUE
, аFALSE
иNULL
— нет, происходит эдакое впихивание трёх значенийOption<Bool>
в два значенияBool
.Я думаю о том, что блок с условием
NaN == NaN
не выполнится, по аналогии с тем, как вSQL
не отберутся строки сNULL = NULL
.Кстати, читал тут документацию, и почему-то
NaN ** 0
в Javascript даёт единицу. Вероятно, потому что нулевая степень чего угодно, даже жаренной с грибами картошки, даёт всё равно единицу.Был бы явный Option, было бы логично:
(Some(5) == Some(5)) == Some(true)
, при этом(Some(5) == None) == None)
и(None == None) = None)
.Но
Option
тут половинчатый, и такое решение выглядит довольно логично. В конце концов,NaN
представляет разные ситуации не-чисел, например,parseInt("blah")
и"".charCodeAt(1)
. Было бы странно говорить, что они равны в контексте сравнения чисел.Много и по делу, спасибо! Если бы хабрамаркетологи следовали хотя бы части упомянутых рекомендаций...
Одно только резануло: милый крабик Rust он, конечно, симпатичный, но гораздо больший эмоциональный отклик у меня вызывает красота концепций, положенных в основу языка. И без крабика я точно так же бы его любил.
А вот не менее милый суслик Go меня скорее раздражает, потому что мне не нравится дизайн языка (и, получается, поэтому даже хороший маркетинг для меня не работает, как это и описано в статье).
UPD: комментарий не предназначен для разжигания. Я просто хочу отметить, что для создания эмоциональной связи с продуктом иногда достаточно и просто кусков кода
Да, согласен, просто хотелось отметить, что Ваша интерактивная визуализация обладает такой же «магией наглядности», превращающей не самые простые выкладки в нечто совершенно очевидное.
Интерактивное демо просто восхитительное! — сразу всё становится понятно. И ещё вспомнилось видео про спирали из простых чисел, не оно ли послужило источником вдохновения?
"Морфологию волшебной сказки" Проппа почему-то не вспомнили
То-то в местной Пятёрочке почти два месяца висело объявление, что карты лояльности временно не работают.
А это, оказывается, внеплановая миграция.
Интересно, что же так нестабильно работало?
Кажется, этот аргумент применим вообще к любому мало-мальски нетривиальному коду.
Задачу я сформулировал следующим образом (и как бы мне теоретически мог сформулировать её коллега-маркетолог): "Интересно, кто что себе покупает на ДР", и условие в запросе в точности соответствует постановке.
Для меня это очевидно не из-за того, что я это писал, а благодаря тому, что я давно привык к подобному поведению SQL и ожидаю именно его. Это не побочный эффект и не баг, это часть стандарта.
Что касается моделирования задачи, то условие «у некоторых пользователей есть дата рождения» полностью соответствует nullable-колонке в таблице с пользователями, разве нет?
Насчёт правильного условия в
JOIN
— согласен, сравнение дат лучше воWHERE
.Тем не менее, для меня очевидно и ожидаемо, что
WHERE user.bdate = DATE(order.ctime)
отбросит нулевые значения.Да хоть во
WHERE
, не нужны дополнительные проверки наNULL
, потому что то же равенство даст тот жеNULL
и строки не будут выбраныА как по-вашему
1 > NULL
— это истина или ложь? А1 < NULL
?Если я хочу сравнить с числом средний чек пользователя, а у него покупок нет, то
AVG(amount)
будетNULL
, даже если вообще вся база у меня спроектирована без нулевых колонок.Как ещё предложите обрабатывать такие случаи?
Допустим, табличка с пользователями, у некоторых есть дата рождения, и табличка с заказами, в которых есть дата оплаты, но только в том случае, если он оплачен.
Интересно, кто что себе покупает на ДР.
Типа:
ON user.id = order.user_id AND user.bdate = DATE(order.paid_at)
.В реальности я бы скорее смотрел заказы за неделю до дня рождения, т. е.
BETWEEN bd - INTERVAL 7 DAY AND bd
, и всё бы тоже прекрасно работало.Вам ни разу не приходилось делать
JOIN
по nullable-колонкам? Если ко всемNULL
ам из одной таблицы приджойнятся всеNULL
ы из другой, будет хорошо и правильно?Почему Вы упорно пытаетесь свести
NOT DISTINCT
к равенству/идентичности? Это просто отдельный концепт.Если уж говорить об аналогиях
NULL
среди типов данных языков программирования, то для меня это всегда казалось чем-то близким монаде MayBe или типуOption
.А поскольку
NULL
может случиться где угодно, вOption
получаются неявно завёрнуты вообще все значения.И distinct/равенство, отлично вписываются в эту концепцию: distinct сравнивает сами
Option
'ы (и возвращаетbool
), а равенство — их содержимое (и возвращает тожеOption
). А вот в JS ничего подобного нет.В одном из ответов на stackoverflow приведены интересные цитаты из спецификации SQL. Если кратко:
NULL
друг другу не равны (not equal), но при этом они неразличимы (not distinct). Таким образом,GROUP BY
не попирает догму, а просто использует иную концепцию.Собственно, SQL2003 определяет оператор
IS DISTINCT FROM
, а в MySQL есть<=>
Если
NULL
будет равен сам себе, то внешние соединения по нулевым полям дадут совершенно неинтуитивный результат. Отсутствующее отчество у Джона Смита — это не то же, что отсутствующее отчество у Джимми ВонгаВот только 0,001 секунд без
fwirte
достигается из-за того, что умный компилятор вырезает вообще все вычисления, потому что их результат никак не используется.