return value - Cómo obtener el "valor" de un tal vez en Haskell
return-value maybe (5)
Desde el Prelude
estándar,
maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing = n
maybe _ f (Just x) = f x
Dado un valor predeterminado, y una función, aplique la función al valor en el Maybe
o devuelva el valor predeterminado.
Su eliminate
podría escribirse maybe 0 id
, por ejemplo, aplicar la función de identidad, o devolver 0.
Del estándar Data.Maybe
,
fromJust :: Maybe a -> a
fromJust Nothing = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x
Esta es una función parcial (no devuelve un valor para cada entrada, a diferencia de una función total , que sí lo hace), pero extrae el valor cuando es posible.
Soy relativamente nuevo en Haskell y comencé a leer "Real World Haskell". Acabo de tropezar con el tipo Quizás tengo una pregunta sobre cómo recibir el valor real de un "Just 1", por ejemplo. He escrito el siguiente código:
combine a b c = (eliminate a, eliminate b, eliminate c)
where eliminate (Just a) = a
eliminate Nothing = 0
Esto funciona bien si uso:
combine (Just 1) Nothing (Just 2)
Pero si cambio, por ejemplo, 1 a una Cadena, no funciona. Creo que sé por qué: porque eliminate
tiene que devolver un tipo, que es, en este caso, un Int
. Pero, ¿cómo puedo cambiar eliminate
para tratar al menos con cadenas? (¿o tal vez con todo tipo de tipos?)
Eso es bastante obvio. La firma de tipo de la función "eliminar" es:
eliminate :: Maybe Int -> Int
Esto se debe a que devuelve 0 en Nothing, lo que obliga al compilador a suponer que "a :: Int" en su función "eliminar". Por lo tanto, el compilador deduce la firma de tipo de la función "combinar" para ser:
combine :: Maybe Int -> Maybe Int -> Maybe Int -> (Int, Int, Int)
y eso es precisamente por lo que no funciona cuando pasas un String a él.
Si lo escribió como:
combine a b c = (eliminate a, eliminate b, eliminate c)
where eliminate (Just a) = a
eliminate Nothing = undefined
entonces hubiera funcionado con String o con cualquier otro tipo. La razón se basa en el hecho de que "undefined :: a", que hace que "elimine" a los polimórficos y aplicable a tipos distintos a los de Int.
Por supuesto, ese no es el objetivo de su código, es decir, hacer que la función de combinación sea total.
De hecho, incluso si una aplicación de "combinar" con algunos argumentos de Nothing tuviera éxito (eso es porque Haskell es flojo por defecto), tan pronto como intentes evaluar los resultados obtendrás un error en tiempo de ejecución ya que "indefinido" no puede ser evaluado a algo útil (para ponerlo en términos simples).
Esta es la respuesta que estaba buscando cuando llegué a esta pregunta:
https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Maybe.html#v:fromJust
... y del mismo modo, para Either
:
https://hackage.haskell.org/package/either-unwrap-1.1/docs/Data-Either-Unwrap.html
Proporcionan funciones que yo mismo habría escrito que desenvuelven el valor de su contexto.
Soy nuevo en Haskell también, así que no sé si esto todavía existe en la plataforma (estoy seguro de que sí), pero ¿qué tal una función "obtener o no" para obtener un valor si existe, de lo contrario, regresar un defecto?
getOrElse::Maybe a -> a -> a
getOrElse (Just v) d = v
getOrElse Nothing d = d
[Editar del autor, 6 años después] Esta es una respuesta innecesariamente larga, y no estoy seguro de por qué fue aceptada. Use maybe
o Data.Maybe.fromMaybe
como se sugiere en la respuesta más alta con voto arriba. Lo que sigue es más un experimento mental que un consejo práctico.
Entonces, estás tratando de crear una función que funcione para muchos tipos diferentes. Este es un buen momento para hacer una clase. Si ha programado en Java o C ++, una clase en Haskell es como una interfaz en esos idiomas.
class Nothingish a where
nada :: a
Esta clase define un valor nada
, que se supone que es el equivalente de la clase de Nothing
. Ahora la parte divertida: hacer instancias de esta clase!
instance Nothingish (Maybe a) where
nada = Nothing
Para un valor de tipo Maybe a
, el valor Nothing es, bueno, Nothing
. Este será un ejemplo extraño en un minuto. Pero antes de eso, hagamos de las listas una instancia de esta clase también.
instance Nothingish [a] where
nada = []
Una lista vacía es algo así como Nada, ¿verdad? Entonces, para una Cadena (que es una lista de Char), devolverá la cadena vacía, ""
.
Los números también son una implementación fácil. Ya has indicado que 0 obviamente representa "Nada" para los números.
instance (Num a) => Nothingish a where
nada = 0
Esto realmente no funcionará a menos que pongas una línea especial en la parte superior de tu archivo
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
O cuando lo compila, puede establecer las banderas para estos pragmas de idioma. No te preocupes por ellos, solo son magia que hace que más cosas funcionen.
Así que ahora que tienes esta clase y estas instancias ... ¡ahora vamos a volver a escribir tu función para usarla!
eliminate :: (Nothingish a) => Maybe a -> a
eliminate (Just a) = a
eliminate Nothing = nada
Note que solo cambié 0
a nada
, y el resto es lo mismo. Vamos a darle un giro!
ghci> eliminate (Just 2)
2
ghci> eliminate (Just "foo")
"foo"
ghci> eliminate (Just (Just 3))
Just 3
ghci> eliminate (Just Nothing)
Nothing
ghci> :t eliminate
eliminate :: (Nothingish t) => Maybe t -> t
ghci> eliminate Nothing
error! blah blah blah...**Ambiguous type variable**
Se ve muy bien para los valores y esas cosas. Observe que (Just Nothing) se convierte en Nothing, ¿ves? Ese fue un ejemplo extraño, un Tal vez en un Tal vez. De todos modos ... ¿qué hay de eliminate Nothing
? Bueno, el tipo resultante es ambiguo. No sabe lo que estamos esperando. Entonces tenemos que decirle qué tipo queremos.
ghci> eliminate Nothing :: Int
0
Adelante y pruébalo para otros tipos; verá que obtiene nada
para cada uno. Entonces, cuando usas esta función con tu función de combine
, obtienes esto:
ghci> let combine a b c = (eliminate a, eliminate b, eliminate c)
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
(2,"foo",Just 3)
ghci> combine (Just 2) Nothing (Just 4)
error! blah blah Ambiguous Type blah blah
Observe que todavía tiene que indicar de qué tipo es "Nada" o indicar qué tipo de devolución espera.
ghci> combine (Just 2) (Nothing :: Maybe Int) (Just 4)
(2,0,4)
ghci> combine (Just 2) Nothing (Just 4) :: (Int, Int, Int)
(2,0,4)
O bien, podría restringir los tipos que su función permite al poner su firma de tipo explícitamente en la fuente. Esto tiene sentido si el uso lógico de la función sería que solo se usa con parámetros del mismo tipo.
combine :: (Nothingish a) => Maybe a -> Maybe a -> Maybe a -> (a,a,a)
combine a b c = (eliminate a, eliminate b, eliminate c)
Ahora solo funciona si las tres cosas de Maybe son del mismo tipo. De esta forma, inferirá que Nothing
es del mismo tipo que los demás.
ghci> combine (Just 2) Nothing (Just 4)
(2,0,4)
Sin ambigüedad, ¡yay! Pero ahora es un error mezclar y combinar, como lo hicimos antes.
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
error! blah blah Couldn''t match expected type blah blah
blah blah blah against inferred type blah blah
Bueno, creo que fue una respuesta suficientemente larga y exagerada. Disfrutar.