Узнай, что у вас Haskell представлен тип Bool
:
data Bool = False | True deriving (Ord)
Я не понимаю причину сравнения Bool
.
> False `compare` True
LT
> True `compare` False
GT
Что было бы потеряно, если Bool
не вышло из Ord
?
Узнай, что у вас Haskell представлен тип Bool
:
data Bool = False | True deriving (Ord)
Я не понимаю причину сравнения Bool
.
> False `compare` True
LT
> True `compare` False
GT
Что было бы потеряно, если Bool
не вышло из Ord
?
Bool
образует ограниченную решетку , где False
внизу, а True
- верхняя. Эта ограниченная решетка определяет (полный) порядок, где False
действительно строго меньше True
. (Они также являются единственными элементами этой решетки.)
Булевы операции and
и or
также могут рассматриваться как встречающиеся и объединенные соответственно в этой решетке. Встреча находит наибольшую нижнюю границу, и соединение находит наименьшую верхнюю границу. Это означает, что a && False = False
- это то же самое, что и нижняя граница дна и всего остального - внизу, а a || True = True
- это то же самое, что и верхняя граница вершины, а что-то верхняя. Таким образом, встреча и объединение, которые используют свойство упорядочения логических элементов, эквивалентны булевым операциям, с которыми вы знакомы.
Вы можете использовать min
и max
, чтобы показать это в Haskell:
False `min` True = False -- this is the greatest lower bound
False && True = False -- so is this
False `max` True = True -- this is the least upper bound
False || True = True -- so is this
Это показывает, что вы можете определить &&
и ||
только из производного экземпляра Ord
:
(&&) = min
(||) = max
Обратите внимание, что эти определения не эквивалентны при наличии другого типа дна, поскольку (&&)
и (||)
являются короткозамкнутыми (нестрогими в второй аргумент, когда первый False
или True
, соответственно), а min
и max
не являются.
Кроме того, небольшая поправка: предложение deriving
не говорит о том, что Bool
"происходит от" Ord
. Он инструктирует GHC получить экземпляр класса Ord
для типа Bool
.
* Более конкретно, дополненная распределительная решетка. Более конкретно, булева алгебра .
Экземпляр Ord
для Bool
становится гораздо более важным, когда вам нужно сравнивать значения, содержащие Bool
где-то внутри. Например, без него мы не сможем писать такие выражения, как:
[False,True] `compare` [False,True,False]
(3, False) < (3, True)
data Person = Person { name :: String, member :: Bool } deriving (Eq, Ord)
и др.
Это потому, что дизайнеры Haskell допустили ошибку! Я никогда не видел учебника по математике, который упоминал порядок булевых. Просто так они могут быть, это не значит, что нужно. Некоторые из нас используют Haskell именно потому, что во многих случаях это запрещает/защищает нас от запутывающих/бессмысленных вещей, но не для этого.
instance Ord Bool
приводит к тому, что a => b
означает, что вы ожидаете a <= b
в значении!
Ранее аргументы в пользу instance Ord Bool
, где вы можете сделать больше типов сопоставимыми неявно. Продолжая эту аргументацию, некоторые могут захотеть сделать все типы сопоставимыми, а также иметь слабое динамическое типирование и вообще опустить классы классов. Но мы хотим, чтобы строгое написание точно запрещало то, что явно не правильно, и instance Ord Bool
побеждает эту цель.
Что касается аргумента, что Bool - ограниченная решетка. В отличие от boolean: = {True, False}, то, что у нас есть в Haskell, есть Bool: = {True, False, bottom} больше не является ограниченной решеткой, так как ни True, ни False не являются элементами идентичности в представлении дна. Это связано с этими комментариями, обсуждающими && vs min и т.д.