Насправді це просто звичайний конструктор даних, який, як правило, визначається в прелюдії , це стандартна бібліотека, яка імпортується автоматично у кожен модуль.
Що може бути, структурно
Визначення виглядає приблизно так:
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. Тоні Хоаре, відомий комп'ютерний вчений (він винайшов Квіксорта і є лауреатом премії Тьюрінга), належить до цього як до своєї "помилки в мільярд доларів" . Тип Можливо - це не єдиний спосіб виправити це, але він виявився ефективним способом це зробити.
Можливо, як функціонер
Ідея перетворення одного типу в інший, щоб операції зі старим типом також можна було перетворити на роботу над новим типом - це концепція, що стоїть під назвою класу типу 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коли ви закінчите.
Можливо, як Монада
Я не впевнений, наскільки ви ще знайомі з концепцією Monadще, але ви принаймні використовували IO aраніше, і підпис типу IO aвиглядає надзвичайно схожим на Maybe a. Хоча IOвін особливий тим, що він не виставляє ваших конструкторів вам і, таким чином, може бути «керований» системою виконання Haskell, він все ще є Functorдодатком до того, що він є Monad. Насправді, є важливий сенс, в якому a Monad- це лише особливий вид Functorз деякими додатковими функціями, але це не місце для цього.
У будь-якому випадку, Monads люблять IOтипи карт до нових типів, які представляють "обчислення, які призводять до значень", і ви можете підняти функції на Monadтипи за допомогою дуже fmapподібної функції, яка називається, liftMщо перетворює звичайну функцію в "обчислення, що призводить до значення, отриманого шляхом оцінки значення функція. "
Ви, мабуть, здогадалися (якщо ви читали це далеко), що Maybeтакож є Monad. Він представляє "обчислення, які не зможуть повернути значення". Як і в fmapприкладі, це дозволяє робити цілу купу обчислень без необхідності чітко перевіряти помилки після кожного кроку. Насправді, так, як побудований Monadекземпляр, обчислення Maybeзначень припиняється, як тільки Nothingзустрічається a , тож це на зразок негайного переривання або безцінного повернення в середині обчислення.
Ви могли б написати, можливо
Як я вже говорив раніше, немає нічого притаманного Maybeтипу, який вкладається в синтаксис мови чи систему виконання. Якщо Haskell не надав його за замовчуванням, ви могли б забезпечити всю його функціональність самостійно! Насправді ви можете все-таки написати це знову самостійно, з різними іменами та отримати однаковий функціонал.
Сподіваємось, ви зараз розумієте Maybeтип та його конструктори, але якщо все ще є щось незрозуміле, дайте мені знати!
Maybeтам, де інші мови використовувались (nullабоnilз неприємнимNullPointerExceptionприхованням у кожному куті). Тепер інші конструкції також починають використовувати цю конструкцію: тип ScalaOptionі навіть Java 8 матимутьOptionalтип.