type recursive maximum funcion else create argument haskell syntax

recursive - maximum in haskell



¿Qué significa la sintaxis "Justa" en Haskell? (5)

Dado un tipo t , un valor de Just t es un valor existente de tipo t , donde Nothing representa un error al alcanzar un valor, o un caso en el que tener un valor no tendría sentido.

En su ejemplo, tener un saldo negativo no tiene sentido, y si eso ocurre, se reemplaza por Nothing .

Para otro ejemplo, esto podría usarse en la división, definiendo una función de división que toma b , y devuelve Just a/b si b es distinto de cero, y Nothing caso contrario. A menudo se usa así, como una alternativa conveniente a las excepciones, o como en el ejemplo anterior, para reemplazar valores que no tienen sentido.

Revisé Internet para obtener una explicación real de lo que hace esta palabra clave. Cada tutorial de Haskell que he analizado simplemente comienza a usarlo al azar y nunca explica qué hace (y he analizado muchos).

Aquí hay una pieza básica de código de Real World Haskell que usa Just . Entiendo lo que hace el código, pero no entiendo cuál es el propósito o la función de Just .

lend amount balance = let reserve = 100 newBalance = balance - amount in if balance < reserve then Nothing else Just newBalance

Por lo que he observado, está relacionado con Maybe typing, pero eso es todo lo que he logrado aprender.

Una buena explicación de lo que Just significa sería muy apreciada.


En realidad, se trata de un constructor de tipos normal que se define en el Preludio , que es la biblioteca estándar que se importa automáticamente en cada módulo.

Lo que tal vez es, estructuralmente

La definición se ve más o menos así:

data Maybe a = Just a | Nothing

Esa declaración define un tipo, Maybe a , que está parametrizado por una variable de tipo a , que simplemente significa que puede usarlo con cualquier tipo en lugar de a .

Construir y Destruir

El tipo tiene dos constructores, Just a y Nothing . Cuando un tipo tiene múltiples constructores, significa que un valor del tipo debe haberse construido con solo uno de los posibles constructores. Para este tipo, un valor se construyó a través de Just o Nothing , no hay otras posibilidades (sin errores).

Como Nothing no tiene ningún tipo de parámetro, cuando se usa como un constructor nombra un valor constante que es miembro del tipo Maybe a para todos los tipos a . Pero el constructor Just 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 tipo a -> Maybe a

Entonces, los constructores de un tipo construyen un valor de ese tipo; el otro lado de las cosas es cuando quisieras usar ese valor, y ahí es donde entra la coincidencia de patrones para jugar. A diferencia de las funciones, los constructores se pueden usar en expresiones de enlace de patrones, y esta es la forma en que puede hacer el análisis de caso de valores que pertenecen a tipos con más de un constructor.

Para usar un valor Maybe a en un patrón, 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 coincide con el segundo, también vincula el nombre val al parámetro que se pasó al constructor Just cuando se construyó el valor con el que coincide.

Lo que tal vez significa

Tal vez ya estabas familiarizado con cómo funcionó esto; no hay realmente ninguna magia para los valores de Maybe , es solo un tipo de datos algebraico Haskell (ADT) normal. Pero se usa bastante porque efectivamente "eleva" o extiende un tipo, como Integer de su ejemplo, a un nuevo contexto en el que tiene un valor extra ( Nothing ) que representa una falta de valor. El sistema de tipo requiere que compruebes ese valor adicional antes de que te permita obtener el Integer que podría estar allí. Esto evita una cantidad notable de errores.

Hoy en día, muchos idiomas manejan este tipo de valor "sin valor" a través de referencias NULL. Tony Hoare, un eminente científico de la computación (inventó Quicksort y es ganador del premio Turing), es hasta este punto su "error billonario" . El tipo Maybe no es la única forma de solucionar esto, pero ha demostrado ser una forma efectiva de hacerlo.

Tal vez como un 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 tipo Haskell llamada Functor , que Maybe a tiene una instancia útil de.

Functor proporciona un método llamado fmap , que mapea las funciones que se extienden sobre los valores del tipo base (como Integer ) a las funciones que se extienden sobre los valores del tipo levantado (como Maybe Integer ). Una función transformada con fmap para trabajar en un valor Maybe 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 valor Maybe Integer m_x y una función Int -> Int f , puede hacer fmap f m_x para 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 levantados Integer -> Integer funciones enteras a los valores Maybe Integer y solo tendrá que preocuparse de comprobar explícitamente Nothing una vez cuando haya terminado.

Tal vez como una Mónada

No estoy seguro de cuán familiarizado estás con el concepto de una Monad aún, pero al menos has usado IO a antes, y la firma de tipo IO a ve notablemente similar a Maybe a . Aunque IO es especial ya que no expone sus constructores a usted y solo puede ser "ejecutado" por el sistema de tiempo de ejecución de Haskell, también es un Functor además de ser una Monad . De hecho, hay un sentido importante en el que una Monad es solo un tipo especial de Functor con algunas características adicionales, pero este no es el lugar para entrar en eso.

De todos modos, las Mónadas como IO asignan tipos a nuevos tipos que representan "cálculos que dan como resultado valores" y usted puede levantar funciones en tipos de fmap través de una función similar a liftM llamada liftM que convierte una función normal en un "cálculo que resulta en el valor obtenido al evaluar la función ".

Probablemente hayas adivinado (si has leído hasta aquí) que Maybe también sea una Monad . Representa "cálculos que podrían no devolver un valor". Al igual que con el ejemplo de fmap , esto le permite hacer una gran cantidad de cálculos sin tener que verificar explícitamente los errores después de cada paso. Y, de hecho, la forma en que se construye la instancia de Monad , un cómputo en los valores Maybe se detiene tan pronto como se encuentra un Nothing , por lo que es como un aborto inmediato o un retorno sin valor en medio de un cálculo.

Podrías haber escrito tal vez

Como dije antes, no hay nada inherente en el tipo Maybe que esté integrado en la sintaxis del lenguaje o el sistema de tiempo de ejecución. ¡Si Haskell no lo proporcionó por defecto, usted mismo podría proporcionar todas sus funcionalidades! De hecho, podría escribirlo usted mismo de todos modos, con diferentes nombres, y obtener la misma funcionalidad.

Espero que entiendas el tipo Maybe y sus constructores ahora, pero si todavía hay algo que no está claro, ¡házmelo saber!


Función if (cond :: Bool) then (ifTrue :: a) else (ifFalse :: a) debe tener el mismo tipo de ifTrue y ifFalse .

Entonces, cuando escribimos then Nothing , debemos usar Maybe a type en else f

if balance < reserve then (Nothing :: Maybe nb) -- same type else (Just newBalance :: Maybe nb) -- same type


La mayoría de las respuestas actuales son explicaciones altamente técnicas de cómo funcionan Just y sus amigos; Pensé que podría intentar explicar de qué se trataba.

Muchos idiomas tienen un valor como null que se puede usar en lugar de un valor real, al menos para algunos tipos. Esto ha enojado a mucha gente y ha sido considerado como un mal movimiento. Aún así, a veces es útil tener un valor como null para indicar la ausencia de algo.

Haskell soluciona este problema al hacer que marques explícitamente lugares donde puedes tener una Nothing (su versión de un null ). Básicamente, si su función normalmente devolvería el tipo Foo , en su lugar debería devolver el tipo Maybe Foo . Si desea indicar que no hay ningún valor, devuelva Nothing . Si desea devolver una bar valor, debería devolver Just bar .

Entonces, básicamente, si no puedes tener Nothing , no necesitas Just . Si puedes tener Nothing , necesitas Just .

No hay nada mágico en Maybe ; está construido sobre el sistema de tipo Haskell. Eso significa que puedes usar todos los trucos habituales de combinación de patrones Haskell con él.


Una función total a-> b puede encontrar un valor de tipo b para cada valor posible de tipo a.

En Haskell no todas las funciones son totales. En este caso particular, la función de lend no es total, no está definida para el caso en que el saldo es menor que la reserva (aunque, a mi parecer, tendría más sentido no permitir que NewBalance sea menor que la reserva). de un saldo de 100).

Otros diseños que tratan con funciones no totales:

  • arrojar excepciones al verificar el valor de entrada no se ajusta al rango
  • devuelve un valor especial (tipo primitivo): la opción favorita es un valor negativo para funciones enteras que están destinadas a devolver números naturales (por ejemplo, String.indexOf - cuando no se encuentra una subcadena, el índice devuelto se suele diseñar como negativo)
  • devuelve un valor especial (puntero): NULL o algo así
  • regresar silenciosamente sin hacer nada: por ejemplo, lend se puede escribir para devolver el saldo anterior, si no se cumple la condición para los préstamos
  • devuelve un valor especial: Nothing (o Left envolviendo algún objeto de descripción de error)

Estas son limitaciones de diseño necesarias en los lenguajes que no pueden hacer cumplir la totalidad de las funciones (por ejemplo, Agda puede, pero eso lleva a otras complicaciones, como volverse incompleto).

El problema de devolver un valor especial o lanzar excepciones es que es fácil para quien llama omitir el manejo de dicha posibilidad por error.

El problema de descartar silenciosamente una falla también es obvio: usted está limitando lo que la persona que llama puede hacer con la función. Por ejemplo, si el lend devolvió el saldo anterior, la persona que llama no tiene forma de saber si el saldo ha cambiado. Puede o no ser un problema, dependiendo del propósito previsto.

La solución de Haskell obliga a la persona que llama de una función parcial a tratar el tipo como Maybe a , o Either error a debido al tipo de devolución de la función.

De esta manera, lend como se define, es una función que no siempre calcula el nuevo saldo; en algunas circunstancias, el saldo nuevo no está definido. Señalamos esta circunstancia a la persona que llama ya sea devolviendo el valor especial Nada, o envolviendo el nuevo saldo en Just. La persona que llama ahora tiene la libertad de elegir: o bien maneja la falla para prestar de una manera especial, o ignora y usa el saldo anterior; por ejemplo, maybe oldBalance id $ lend amount oldBalance .