En realidad, es solo un constructor de datos normal que se define en el Preludio , que es la biblioteca estándar que se importa automáticamente en cada módulo.
Lo que quizás es, estructuralmente
La definición se parece a esto:
data Maybe a = Just a
| Nothing
Esa declaración define un tipo, Maybe a
que está parametrizado por una variable de tipo a
, lo que solo significa que puede usarlo con cualquier tipo en lugar de a
.
Construyendo y Destruyendo
El tipo tiene dos constructores Just a
y Nothing
. Cuando un tipo tiene varios constructores, significa que un valor del tipo debe haberse construido con solo uno de los posibles constructores. Para este tipo, se construyó un valor mediante Just
o Nothing
, no hay otras posibilidades (sin error).
Dado que Nothing
no tiene un tipo de parámetro, cuando se usa como constructor, nombra un valor constante que es un miembro de tipo Maybe a
para todos los tipos a
. Pero el Just
constructor tiene un parámetro de tipo, lo que significa que cuando se usa como constructor actúa como una función de tipo a
a Maybe a
, es decir, tiene el tipoa -> Maybe a
Entonces, los constructores de un tipo construyen un valor de ese tipo; el otro lado de las cosas es cuando le gustaría usar ese valor, y ahí es donde entra en juego la coincidencia de patrones. A diferencia de las funciones, los constructores se pueden usar en expresiones de enlace de patrones, y esta es la forma en que puede realizar análisis de casos de valores que pertenecen a tipos con más de un constructor.
Para usar un Maybe a
valor en una coincidencia de patrones, debe proporcionar un patrón para cada constructor, así:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
En esa expresión de caso, el primer patrón coincidiría si el valor fuera Nothing
, y el segundo coincidiría si el valor se construyera con Just
. Si el segundo coincide, también vincula el nombre val
al parámetro que se pasó al Just
constructor cuando se construyó el valor con el que está haciendo coincidir.
Lo que quizás significa
Quizás ya estaba familiarizado con cómo funcionaba esto; Realmente no hay magia en los Maybe
valores, es solo un tipo de datos algebraicos Haskell (ADT) normal. Pero se usa bastante porque efectivamente "eleva" o extiende un tipo, como el Integer
de su ejemplo, a un nuevo contexto en el que tiene un valor adicional ( Nothing
) que representa una falta de valor. El sistema de tipos requiere que verifique ese valor adicional antes de que le permita obtener el Integer
que podría estar allí. Esto evita una gran cantidad de errores.
Hoy en día, muchos lenguajes manejan este tipo de valor "sin valor" a través de referencias NULL. Tony Hoare, un científico informático eminente (inventó Quicksort y es un ganador del premio Turing), reconoce esto como su "error de mil millones de dólares" . El tipo Maybe no es la única forma de solucionar este problema, pero ha demostrado ser una forma eficaz de hacerlo.
Quizás como Functor
La idea de transformar un tipo en otro de modo que las operaciones en el tipo antiguo también se puedan transformar para trabajar en el nuevo tipo es el concepto detrás de la clase de tipos Haskell llamada Functor
, que Maybe a
tiene una instancia útil de.
Functor
proporciona un método llamado fmap
, que asigna funciones que varían sobre valores desde el tipo base (como Integer
) a funciones que varían sobre valores desde el tipo elevado (como Maybe Integer
). Una función transformada con fmap
para trabajar en un Maybe
valor funciona así:
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
Entonces, si tiene un Maybe Integer
valor m_x
y una Int -> Int
función f
, puede fmap f m_x
aplicar la función f
directamente al Maybe Integer
sin preocuparse si realmente tiene un valor o no. De hecho, podría aplicar una cadena completa de Integer -> Integer
funciones elevadas a los Maybe Integer
valores y solo tendrá que preocuparse por verificar explícitamente Nothing
una vez cuando haya terminado.
Quizás como una mónada
No estoy seguro de qué tan familiarizado está con el concepto de un Monad
todavía, pero al menos lo ha usado IO a
antes, y la firma de tipo se IO a
ve muy similar a Maybe a
. Aunque IO
es especial en el sentido de que no le expone sus constructores y, por lo tanto, solo puede ser "ejecutado" por el sistema de tiempo de ejecución de Haskell, también es un Functor
complemento de Monad
. De hecho, hay un sentido importante en el que a Monad
es solo un tipo especial Functor
con algunas características adicionales, pero este no es el lugar para entrar en eso.
De todos modos, a las mónadas les gustan IO
los tipos de mapas de nuevos tipos que representan "cálculos que dan como resultado valores" y puedes convertir funciones en Monad
tipos a través de una fmap
función muy similar a la llamada liftM
que convierte una función regular en un "cálculo que da como resultado el valor obtenido al evaluar el función."
Probablemente haya adivinado (si ha leído hasta aquí) que Maybe
también es un Monad
. Representa "cálculos que podrían no devolver un valor". Al igual que con el fmap
ejemplo, esto le permite hacer un montón de cálculos sin tener que buscar errores explícitamente después de cada paso. Y de hecho, la forma en que Monad
se construye la instancia, un cálculo de Maybe
valores se detiene tan pronto como Nothing
se encuentra a, por lo que es como un aborto inmediato o una devolución sin valor en medio de un cálculo.
Podrías haber escrito quizás
Como dije antes, no hay nada inherente al Maybe
tipo que está integrado en la sintaxis del lenguaje o en el sistema de ejecución. Si Haskell no lo proporcionó de forma predeterminada, ¡usted mismo podría proporcionar todas sus funciones! De hecho, podría volver a escribirlo usted mismo de todos modos, con diferentes nombres, y obtener la misma funcionalidad.
Espero que entiendas el Maybe
tipo y sus constructores ahora, pero si todavía hay algo que no está claro, ¡avísame!
Maybe
donde otros lenguajes usaríannull
onil
(con desagradablesNullPointerException
s acechando en cada esquina). Ahora, otros lenguajes también comienzan a usar esta construcción: Scala asOption
, e incluso Java 8 tendrá elOptional
tipo.