На самом деле это обычный конструктор данных, который определяется в Prelude , стандартной библиотеке, которая автоматически импортируется в каждый модуль.
Что может быть, структурно
Определение выглядит примерно так:
data Maybe a = Just a
| Nothing
Это объявление определяет тип, Maybe aкоторый параметризуется переменной типа a, что означает лишь то, что вы можете использовать его с любым типом вместо a.
Конструирование и разрушение
Тип имеет два конструктора, Just a и Nothing. Если тип имеет несколько конструкторов, это означает, что значение типа должно быть построено только с одним из возможных конструкторов. Для этого типа значение было либо построено с помощью, Justлибо Nothing, других возможностей (без ошибок) нет.
Поскольку Nothingне имеет типа параметра, когда он используется в качестве конструктора, он называет постоянное значение, которое является членом типа Maybe aдля всех типов.a . Но у Justконструктора есть параметр типа, что означает, что при использовании в качестве конструктора он действует как функция от типа aк Maybe a, то есть имеет типa -> Maybe a
Итак, конструкторы типа создают значение этого типа; другая сторона вещей - это когда вы хотите использовать это значение, и именно здесь в игру вступает сопоставление с образцом. В отличие от функций, конструкторы могут использоваться в выражениях привязки шаблонов, и это способ, которым вы можете выполнять анализ значений, принадлежащих типам с более чем одним конструктором.
Чтобы использовать Maybe aзначение в сопоставлении с шаблоном, вам необходимо предоставить шаблон для каждого конструктора, например:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
В этом случае выражение, первый шаблон будет соответствовать, если значение было Nothing, и второй будет соответствовать, если значение было построено с Just. Если второй совпадает, он также связывает имяval с параметром, который был передан Justконструктору при построении значения, с которым вы сопоставляете.
Что может означать
Возможно, вы уже были знакомы с тем, как это работает; в значениях нет никакого волшебства Maybe, это просто обычный алгебраический тип данных Haskell (ADT). Но он используется довольно часто, потому что он эффективно «поднимает» или расширяет тип, например, Integerиз вашего примера, в новый контекст, в котором он имеет дополнительное значение ( Nothing), которое представляет собой отсутствие значения! Затем система типов требует, чтобы вы проверили это дополнительное значение, прежде чем оно позволит вам найти то, Integerчто может быть там. Это предотвращает значительное количество ошибок.
Многие языки сегодня обрабатывают такого рода значения «без значения» через ссылки NULL. Тони Хоар, выдающийся ученый-компьютерщик (он изобрел Quicksort и является лауреатом премии Тьюринга), считает это своей «ошибкой на миллиард долларов» . Тип Maybe - не единственный способ исправить это, но он оказался эффективным способом сделать это.
Может быть как функтор
Идея преобразования одного типа в другой таким образом, чтобы операции со старым типом также могли быть преобразованы для работы с новым типом, лежит в основе вызываемого класса типов Haskell Functor, который Maybe aимеет полезный экземпляр.
Functorпредоставляет вызываемый метод fmap, который отображает функции, которые варьируются от значений базового типа (например, Integer) до функций, которые варьируются от значений из поднятого типа (например, Maybe Integer). Функция, преобразованная fmapдля работы со Maybeзначением, работает следующим образом:
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
Итак, если у вас есть Maybe Integerзначение m_xи Int -> Intфункция f, вы можете fmap f m_xприменить эту функцию fнепосредственно к Maybe Integerобъекту, не беспокоясь о том, действительно ли оно имеет значение или нет. Фактически, вы можете применить к значениям целую цепочку расширенных Integer -> Integerфункций, Maybe Integerи вам нужно будет только беспокоиться о явной проверке Nothingтолько один раз, когда вы закончите.
Может быть, как монада
Я еще не уверен, насколько вы знакомы с концепцией a Monad, но вы, по крайней мере, использовали его IO aраньше, и подпись типа IO aвыглядит удивительно похожей на Maybe a. Несмотря IOна то, что он особенный в том, что он не предоставляет вам свои конструкторы и, таким образом, может «запускаться» только системой времени выполнения Haskell, он по-прежнему является не Functorтолько файлом Monad. На самом деле, есть важное значение, в котором a Monad- это просто особый видFunctor с некоторыми дополнительными функциями, но здесь не место для этого.
В любом случае, монады любят IOсопоставлять типы с новыми типами, которые представляют «вычисления, которые приводят к значениям», и вы можете преобразовывать функции в Monadтипы с помощью очень fmapпохожей функции, называемой liftM которая превращает обычную функцию в «вычисление, которое приводит к значению, полученному путем оценки функция «.
Вы, наверное, догадались (если дочитали до этого места), что Maybeэто тоже Monad. Он представляет собой «вычисления, которые могут не вернуть значение». Как и в fmapпримере, это позволяет вам выполнять целый ряд вычислений без необходимости явно проверять наличие ошибок после каждого шага. Фактически, при построении Monadэкземпляра вычисление Maybeзначений останавливается, как только Nothingвстречается a , так что это похоже на немедленное прерывание или возврат без значения в середине вычисления.
Вы могли бы написать "Может быть"
Как я уже говорил ранее, Maybeтипу не присуще ничего, что встроено в синтаксис языка или систему времени выполнения. Если бы Haskell не предоставлял его по умолчанию, вы могли бы предоставить все его функции самостоятельно! Фактически, вы все равно можете написать его снова, с другими именами, и получить ту же функциональность.
Надеюсь, Maybeтеперь вы понимаете этот тип и его конструкторы, но если все еще что-то неясно, дайте мне знать!
Maybeтам, где другие языки используютnullornil(с неприятнымиNullPointerExceptionсловами, скрывающимися в каждом углу). Теперь другие языки также начинают использовать эту конструкцию: Scala asOption, и даже Java 8 будет иметь этотOptionalтип.