Насправді це просто звичайний конструктор даних, який, як правило, визначається в прелюдії , це стандартна бібліотека, яка імпортується автоматично у кожен модуль.
Що може бути, структурно
Визначення виглядає приблизно так:
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
тип.