W rzeczywistości jest to zwykły konstruktor danych, który jest zdefiniowany w Prelude , która jest standardową biblioteką importowaną automatycznie do każdego modułu.
Co może jest strukturalnie
Definicja wygląda mniej więcej tak:
data Maybe a = Just a
| Nothing
Ta deklaracja definiuje typ, Maybe aktóry jest sparametryzowany przez zmienną typu a, co oznacza po prostu, że można go używać z dowolnym typem zamiast a.
Konstruowanie i niszczenie
Typ ma dwa konstruktory Just ai Nothing. Gdy typ ma wiele konstruktorów, oznacza to, że wartość typu musi zostać skonstruowana za pomocą tylko jednego z możliwych konstruktorów. W przypadku tego typu wartość została utworzona za pośrednictwem Justlub Nothing, nie ma innych (bezbłędnych) możliwości.
Ponieważ Nothingnie ma typu parametru, gdy jest używany jako konstruktor, nazywa stałą wartość, która jest członkiem typu Maybe adla wszystkich typów a. Ale Justkonstruktor ma parametr typu, co oznacza, że używany jako konstruktor zachowuje się jak funkcja od typu ado Maybe a, tj. Ma typa -> Maybe a
Zatem konstruktory typu budują wartość tego typu; drugą stroną jest sytuacja, w której chciałbyś użyć tej wartości, i to jest moment, w którym pojawia się dopasowywanie wzorców. W przeciwieństwie do funkcji konstruktorów można używać w wyrażeniach wiążących wzorce i jest to sposób, w jaki można przeprowadzić analizę przypadków wartości należących do typów z więcej niż jednym konstruktorem.
Aby użyć Maybe awartości w dopasowaniu do wzorca, musisz podać wzorzec dla każdego konstruktora, na przykład:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
W tym przypadku wyrażenie, pierwszy wzorzec pasowałby, gdyby wartość była Nothing, a drugi byłby dopasowany, gdyby wartość została skonstruowana za pomocą Just. Jeśli druga jest zgodna, wiąże również nazwę valz parametrem, który został przekazany do Justkonstruktora, gdy została skonstruowana wartość, do której pasuje.
Co może oznacza
Może już wiesz, jak to działa; wartości nie mają żadnej magii Maybe, to po prostu zwykły algebraiczny typ danych Haskella (ADT). Ale jest używany dość często, ponieważ skutecznie „podnosi” lub rozszerza typ, taki jak Integerz twojego przykładu, do nowego kontekstu, w którym ma dodatkową wartość ( Nothing), która reprezentuje brak wartości! System typu następnie wymaga sprawdzenia tej dodatkowej wartości, zanim będzie pozwalają uzyskać u Integer, że może tam być. Zapobiega to znacznej liczbie błędów.
Obecnie wiele języków obsługuje tego rodzaju wartości „bez wartości” za pośrednictwem odwołań NULL. Tony Hoare, wybitny informatyk (wynalazł Quicksort i jest laureatem nagrody Turinga), przyznaje się do tego jako „miliardowy błąd” . Typ Może nie jest jedynym sposobem, aby to naprawić, ale okazał się skutecznym sposobem na zrobienie tego.
Może jako funktor
Pomysł przekształcenia jednego typu na inny, tak aby operacje na starym typie można było również przekształcić, aby działały na nowym typie, jest koncepcją wywoływaną przez klasę typu Haskell Functor, która Maybe ama przydatne wystąpienie.
Functorudostępnia metodę wywoływaną fmap, która mapuje funkcje, których zakres obejmuje wartości od typu podstawowego (na przykład Integer) do funkcji, których zakres obejmuje wartości z typu zniesionego (na przykład Maybe Integer). Funkcja przekształcona w fmapcelu pracy z Maybewartością działa w ten sposób:
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
Więc jeśli masz Maybe Integerwartość m_xi Int -> Intfunkcję f, możesz fmap f m_xzastosować funkcję fbezpośrednio do funkcji Maybe Integerbez martwienia się, czy rzeczywiście ma wartość, czy nie. W rzeczywistości możesz zastosować cały łańcuch podniesionych Integer -> Integerfunkcji do Maybe Integerwartości i musisz się martwić tylko o jawne sprawdzenie Nothingraz, kiedy skończysz.
Może jako monada
Nie jestem pewien, jak dobrze znasz pojęcie a Monad, ale przynajmniej używałeś go IO awcześniej, a podpis typu IO awygląda niezwykle podobnie do Maybe a. Chociaż IOjest wyjątkowy, ponieważ nie ujawnia swoich konstruktorów przed tobą i dlatego może być "uruchamiany" tylko przez system wykonawczy Haskell, nadal jest również Functordodatkiem do bycia Monad. W rzeczywistości istnieje ważny sens, w którym a Monadjest po prostu specjalnym rodzajem Functorz kilkoma dodatkowymi funkcjami, ale to nie jest miejsce, aby się tym zająć.
W każdym razie, monady lubią IOodwzorowywać typy na nowe typy, które reprezentują „obliczenia, które dają w wyniku wartości” i możesz podnieść funkcje do Monadtypów za pomocą bardzo fmappodobnej funkcji zwanej, liftMktóra zamienia zwykłą funkcję w „obliczenie, którego wynikiem jest wartość uzyskana przez oszacowanie funkcjonować."
Prawdopodobnie zgadłeś (jeśli przeczytałeś tak daleko), że Maybejest to również plik Monad. Reprezentuje „obliczenia, które mogą nie zwrócić wartości”. Podobnie jak w fmapprzykładzie, pozwala to wykonać całą masę obliczeń bez konieczności jawnego sprawdzania błędów po każdym kroku. I faktycznie, sposób, w jaki Monadinstancja jest skonstruowana, obliczanie Maybewartości zatrzymuje się, gdy tylko Nothingzostanie napotkane, więc jest to trochę jak natychmiastowe przerwanie lub bezwartościowy zwrot w środku obliczenia.
Mogłeś napisać może
Jak powiedziałem wcześniej, nie ma nic nieodłącznego od Maybetypu, który jest wbudowany w składnię języka lub system wykonawczy. Jeśli Haskell nie zapewniłby go domyślnie, możesz sam zapewnić całą jego funkcjonalność! W rzeczywistości i tak możesz napisać go ponownie, pod różnymi nazwami i uzyskać tę samą funkcjonalność.
Mamy nadzieję, że rozumiesz teraz Maybetyp i jego konstruktorów, ale jeśli nadal jest coś niejasnego, daj mi znać!
Maybetam, gdzie inne języki używająnulllubnil(z paskudnymNullPointerExceptionczającym się za każdym rogiem). Teraz inne języki również zaczynają używać tej konstrukcji: Scala asOption, a nawet Java 8 będzie miała tenOptionaltyp.