Другой способ смотреть на него (в дополнение к ответу Тихона), чтобы рассмотреть еще один из основных типов Haskell, Either
, которая определяется следующим образом:
-- | A value that contains either an @[email protected] (the 'Left') constructor) or
-- a @[email protected] (the 'Right' constructor).
data Either a b = Left a | Right b
Это позволяет иметь значения, как это:
example1, example2 :: Either String Int
example1 = Left "Hello!"
example2 = Right 42
... но как это одна:
example3, example4 :: Either String String
example3 = Left "Hello!"
example4 = Right "Hello!"
Тип Either String String
, в первый раз, когда вы сталкиваетесь с этим, звучит как «либо String
, либо String
», и поэтому вы можете подумать, что это то же самое, что и String
. Но это не так, потому что союзы Haskell составляют с тегами союзов, и поэтому Either String String
записывает не только String
, но также и те из «тегов» (конструкторы данных, в данном случае Left
и Right
). Поэтому, хотя обе альтернативы несут String
в качестве своей полезной нагрузки, вы можете сказать, как было построено какое-то одно значение. Это хорошо, потому что есть много случаев, когда альтернативы имеют тот же тип, но конструкторы/теги придают дополнительный смысл:
data ResultMessage = FailureMessage String | SuccessMessage String
Здесь Конструкторы данные являются FailureMessage
и SuccessMessage
, и вы можете догадаться из названия, что даже хотя полезная нагрузка в обоих случаях равна String
, они означают совсем другие вещи!
Так вернуть его к Maybe
/Just
, что тут происходит, что Haskell просто равномерно работает так: каждая альтернатива типа союза имеет ярко выраженный конструктор данных, которые всегда должны быть использованы для построения и значения сопоставления с образцом его типа , Даже если сначала вы могли бы подумать, что можно было бы угадать это из контекста, это просто не делает этого.
Есть и другие причины, немного более технические. Во-первых, правила для ленивой оценки определяются с точки зрения конструкторов данных. Короткий вариант: ленивая оценка означает, что если Haskell вынужден заглянуть внутрь значения Maybe a
, он попытается выполнить минимальный объем работы, необходимый для выяснения, похоже ли это на Nothing
или как Just x
- возможно, t заглянуть внутрь x
когда он делает это.
Во-вторых: язык должен быть способен различать такие типы, как Maybe a
, Maybe (Maybe a)
и Maybe (Maybe (Maybe a))
. Если вы думаете об этом, если у нас есть определение типа, которое работает так, как вы писали:
data Maybe a = Nothing | a -- NOT VALID HASKELL
...и мы хотели, чтобы значение типа Maybe (Maybe a)
, вы не могли бы сказать, эти два значения друг от друга:
example5, example6 :: Maybe (Maybe a)
example5 = Nothing
example6 = Just Nothing
Это может показаться немного глупым сначала, но представьте себе, у вас есть карта, значения которого являются " обнуляемым ":
-- Map of persons to their favorite number. If we know that some person
-- doesn't have a favorite number, we store `Nothing` as the value for
-- that person.
favoriteNumber :: Map Person (Maybe Int)
... и хочу посмотреть запись:
Map.lookup :: Ord k => Map k v -> k -> Maybe v
Так что, если мы посмотрим на mary
на карте мы имеем:
Map.lookup favoriteNumber mary :: Maybe (Maybe Int)
И теперь результат Nothing
означает Мэри не на карте, в то время как Just Nothing
означает Марии на карте, но она не имеет любимый номер.
Значит, это своего рода (преднамеренный) непредвиденный, в некотором смысле? Язык требует конструктора, и один из них позволяет совпадать с шаблоном 'Just _' и' isJust' и т. Д. – orome
@raxacoricofallapatorius: справа. Это осознанное дизайнерское решение с некоторыми сильными результатами. Помимо сопоставления с образцом, это также помогает, в частности, с выводами типа. –
Да, немного дерева, трясущегося от дизайнеров. – orome