This is actually a regular data constructor, which is defined in Prelude, which is a standard library that is automatically imported into each module.
What could be structurally
The definition looks something like this:
data Maybe a = Just a | Nothing
This declaration defines a type, Maybe a , which is parameterized by a variable of type a , which means that you can use it with any type instead of a .
Construction and Destruction
The type has two constructors, Just a and Nothing . When a type has several constructors, this means that the type value must be created with only one of the possible constructors. For this type, the value was created using Just or Nothing , there are no other (without errors) possibilities.
Since Nothing does not have a parameter type, when used as a constructor, it names a constant value that is a member of type Maybe a for all types a . But the Just constructor has a type parameter, which means that when used as a constructor, it acts as a function from a type to Maybe a , that is, it has type a → Maybe a
So, type constructors create a value for that type; the other side is when you want to use this value, and it is here that pattern matching comes into play. Unlike functions, constructors can be used in template binding expressions, and in this way you can parse values belonging to types with more than one constructor.
To use the Maybe a value in comparison with the sample, you must provide a template for each constructor, for example, like this:
case maybeVal of Nothing -> "There is nothing!" Just val -> "There is a value, and it is " ++ (show val)
In this case, the expression will match the first pattern if the value is Nothing , and the second will match if the value was created using Just . If the second one matches, it also associates the name val with the parameter that was passed to the Just constructor when the value you were comparing with was created.
What could be mean
Maybe you were already familiar with how this works; in fact, there is no magic for Maybe values, it's just a normal Haskell algebraic data type (ADT). But it was used quite a bit because it effectively “lifts” or extends a type, such as the Integer from your example, into a new context in which it has an extra value ( Nothing ) that represents the absence of a value! The type system then requires you to check this extra value before it allows you to get an Integer that might be there. This prevents a significant amount of errors.
Many languages today process this type of value "no value" through NULL references. Tony Hoar, an outstanding computer scientist (he invented Quicksort and is a winner of the Turing Prize), admits this as his "billion-dollar mistake . " Maybe is not the only way to fix this, but it has proven to be an effective way to do this.
Maybe like a functor
The idea of converting one type to another so that operations on the old type can also be converted to work with the new type is the concept of a Haskell type class called Functor , which, Maybe a has a useful instance.
Functor provides a method called fmap that maps functions that range in value from a base type (e.g., Integer ) to functions that range in value from a raised type (e.g. Maybe Integer ). The function converted with fmap to work with the Maybe value works as follows:
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
Therefore, if you have a Maybe Integer m_x value and an Int → Int f function, you can execute fmap f m_x to apply the f function directly to Maybe Integer without worrying about whether it got the value or not. In fact, you can apply a whole chain of raised Integer → Integer functions to Maybe Integer values and you only have to worry about explicitly checking Nothing once when you are done.
Maybe like a monad
I'm not sure how familiar you are with the Monad concept, but at least you've already used IO a , and a signature like IO a looks amazingly similar to Maybe a . Although IO is special in that IO that it does not provide you with its constructors and thus can only be "started" by the Haskell runtime system, it is still also a Functor in addition to Monad . In fact, there is an important point in which Monad is just a special kind of Functor with some additional features, but it’s not a place to figure it out.
In any case, monads like IO map types to new types that represent "calculations that yield values", and you can raise functions into Monad types using the fmap -like function itself, called liftM which turns a regular function into "calculation, which leads to the value obtained by evaluating the function. "
You probably guessed (if you read this far) that Maybe it is also Monad . It represents "calculations that may not return a value." As in the fmap example, this allows you to do a whole bunch of calculations without having to explicitly check for errors after each step. And in fact, like a Monad instance of Monad , the calculation of Maybe values ends as soon as Nothing is encountered, so it's kind of an immediate interrupt or a useless return in the middle of the calculation.
Could you write maybe
As I said, there is nothing inherent to the Maybe type that is built into the language syntax or runtime system. If Haskell did not provide it by default, you can provide all its functions yourself! In fact, you could still write it yourself, with different names, and get the same functionality.
I hope you understand the Maybe type and its constructors now, but if something is unclear, let me know!