tipos reales funciones ejemplos definir como bounded types signature haskell

types - reales - Tipo-firma vs. función ecuación en Haskell



reales en haskell (5)

El operador -> es asociativo de la derecha, es decir, t -> t -> t es lo mismo que t -> (t -> t) .

Si vuelves a escribir

f x y = x+y

en la forma equivalente

f x = /y -> x + y

esta connceción debería ser obvia.

Soy nuevo en Haskell y en la programación funcional. Estoy leyendo Real World Haskell, y me he dado cuenta de que estoy confundido por algunos ejemplos.

Específicamente, esto es en el Capítulo 9, en la sección "Un lenguaje específico del dominio para predicados", los ejemplos que tienen los parámetros de wxyz.

Lo he resumido a esto:

¿Por qué compila este código?

f :: Int -> (Int -> Int) f x y = x+y main = do let q = f 4 5 putStr (show (q))

De acuerdo con la firma de tipo, f acepta claramente 1 parámetro y devuelve una función. Sin embargo, parece que puedo escribir la ecuación de función para que acepte dos parámetros y devuelva un int. ¿Por qué es esto posible? ¿Esto significa que se ignora la firma de tipo?

Es esto currying? ¿Es esto algún tipo de cierre? Si entiendo esto http://www.haskell.org/haskellwiki/Currying correctamente, entonces parece ser algo en reversa al currying como se define allí - ¡mi función f toma múltiples argumentos en lugar de uno solo!

Además, si alguien puede responder, proporcione un enlace a algún tipo de documentación de Haskell donde se establezca esta capacidad (si es posible).

EDITAR:

Después de pensar en esto por un tiempo, lo que ustedes dos parecen estar implicando es que:

1) Esta sintaxis es azúcar sintáctica, f siempre tendrá un único parámetro, sin importar cuántos parámetros estén escritos en la ecuación

2) Al aplicar f, el cuerpo de la función se transformará (¿siempre?) En un stub (en efecto, la función devuelta), donde x se fija al parámetro dado (4) e y es un parámetro.

3) Entonces esta nueva función se aplica a 5 que reemplaza a y, y luego se evalúa la función +.

Lo que realmente me interesó fue, dónde exactamente dice algo como "en la ecuación de función, si escribes más de un parámetro, es realmente azúcar sintáctica, y sucede lo siguiente ..." como escribí anteriormente. ¿O es tan obvio para todos menos para mí?

Editar II:

La verdadera respuesta reveladora fue el comentario de @luqui a continuación, lamentablemente no creo que pueda marcar un comentario como respuesta.

Es el hecho de que fxy = ... en realidad es azúcar sintáctica para: f = / x -> / y -> ...

Y para mí, todo lo demás que todo el mundo dice a continuación se sigue de esto.

Encontré una especie de fuente para esto en la Introducción suave a Haskell, aquí: http://haskell.cs.yale.edu/tutorial/functions.html en la sección 3.1, llamada Lambda Abstractions.

De hecho, las ecuaciones:

inc x = x + 1 agregar xy = x + y

son realmente una abreviatura de:

inc = / x -> x + 1 add = / xy -> x + y

Si bien no usa la frase "azúcar sintáctico", utiliza la palabra más, uh, matemáticamente orientada "taquigrafía", pero como programador leo esto como "azúcar" :-)


En realidad, no es la sintaxis del azúcar, sino cómo funciona la aplicación de funciones en Haskell.

Considerar:

f :: Int -> Int -> Int -> Int f x y z = x + y + z g = f 4 h = g 4 5 f 4 4 5 -- 13 g 4 5 -- 13 g 6 -- 13

Puedes jugar con esto en ghci para confirmar. g es una aplicación parcial de la función f - su tipo es g :: Int -> Int -> Int .

También puedes escribir:

((f 4) 4) 5 -- 13

En este caso (f 4) devuelve una función parcialmente aplicada que toma dos argumentos adicionales, ((f 4) 4) devuelve una función parcialmente aplicada que toma un argumento, y la expresión completa se reduce a 13.


Esto definitivamente es un poco curried. Si bien no puedo encontrar de inmediato en qué parte de la documentación se establece explícitamente este comportamiento, todo lo que tenemos que hacer es verificar un poco nuestras matemáticas sobre el currículum.

Como sabemos, una función con la firma Int ->(Int -> Int) toma un Int , y devuelve una función que toma un Int y devuelve un Int. Y podríamos proporcionar los dos Int Que necesita para obtener esa int final, como en su ejemplo:

f :: Int -> (Int -> Int) f x y = x+y

Y si proporcionamos solo el primer argumento, recuperamos una función que necesita otro argumento. El pan y la mantequilla de currying.

En pocas palabras, currying es correcto asociativo . En otras palabras, Int -> (Int -> Int) es lo mismo que Int->Int->Int , solo agregamos paréntesis para que sea más obvio que:

f 3

No falta un argumento, pero en realidad devuelve una función de tipo Int->Int .

Es como volver en matemáticas cuando aprendes la propiedad asociativa de la suma.

3 + 2 + 1 = (3 + 2) + 1 = 3 + (2 + 1)

El resultado es el mismo, independientemente de cómo pongamos nuestros paréntesis.


Después de pensar un poco más sobre esto, creo que la explicación completa debería ser algo así como:

Una función Haskell solo puede aceptar un único argumento y devolver un parámetro. Haskell nos permite pretender que se pasan varios argumentos, pero esta forma se trata como una serie de funciones lambda anidadas.

f x y = x + y

es tratado como

(1) f = /x -> /y -> x + y

Este tratamiento también se aplica a las funciones lambda / xy -> x + y se trata como / x -> / y -> x + y

Esto nos permite tratar la declaración de tipo como asociada a la izquierda, es decir: f :: Int -> Int -> Int es en realidad f :: (Int -> (Int -> Int)) que se ajusta exactamente a (1) arriba: f no tiene argumentos, pero devuelve una función que acepta Int. Esta función a su vez devuelve una función que acepta otro Int, y devuelve un Int.

Esto significa que si queremos devolver una función desde una función, no tenemos que hacer nada especial, ya que ese es el modo "predeterminado" de Haskell.

Esto también significa que dada la declaración de tipo f :: Int -> Int -> Int Podemos escribir la implementación de f ("ecuación") con 0, 1 o 2 parámetros. Si se especifican uno o dos parámetros, el compilador generará las lambdas necesarias para cumplir con la forma f :: (Int -> (Int -> Int))

f = /x -> /y -> x + y f x = /y -> x + y -- /x -> is generated f x y = x + y -- /x -> /y is generated

Pero en cada uno de estos casos, la aplicación de función que parece aceptar dos parámetros se compilará correctamente, ya que siempre se traducirá a la primera forma, por ejemplo

f 4 5 --> (/x -> (/y -> x + y) 5 ) 4

Donde la aplicación de función interna devolverá la forma al curry (x + 5)

Esto permite la aplicación de funciones parciales, donde podemos dar f solo un parámetro y recuperar una función parcial.

Además, cambiando la precedencia en el tipo de función:

f'''' :: (Int -> Int) -> Int

cambia el significado - aceptamos que una función obtenga un Int y regrese uno como el parámetro único, y devuelve un Int.
Suponiendo que hayamos definido esta función en algún lugar, entonces llamamos a esta función con parámetros enteros, por ejemplo

f'''' 4 5

no compilará

Editar:

Además, incluso si los últimos argumentos están entre paréntesis, o son una declaración de tipo, esto se cumple.

Por ejemplo, a continuación, el último par de paréntesis es opcional, ya que si no estuvieran allí, el compilador los pondría de todos modos para llegar al formulario "lambda''d".

t4 :: (Int -> Int) -> (Int -> Int) t4 f i = f i + i

Se puede aplicar así:

t4 (/x -> x*10) 5

Además, dado:

type MyIntInt = Int -> Int

Entonces:

t5 :: MyIntInt -> MyIntInt

Es equivalente a t4, pero confuso, ya que el significado de MyIntInt es diferente en ambos lugares. El primero es el tipo del primer parámetro.
El segundo se "expande" en Int -> Int (probablemente debido a la asociatividad correcta del operador), lo que significa que t5 puede parecer que acepta 0 a 2 parámetros en la función ecuación y aplicación de función (mientras que en realidad siempre acepta 0 y devolver lambdas incrustadas, como he detallado anteriormente).

Por ejemplo, podemos escribir t5 al igual que t4:

t5 f i = f i + i


Es currying. Como dice la firma de tipo, f toma solo un parámetro. Eso sería 4 . Luego devuelve una función, que se aplica inmediatamente a 5 . De hecho, estas dos firmas de tipo:

Int -> Int -> Int

y

Int -> (Int -> Int)

son equivalentes en Haskell.

EDITAR: este enlace sobre la Aplicación parcial , que encontré dentro de la página que proporcionó, explica esto.

EDIT 2: preguntaste dónde se define el comportamiento de Haskell de currículum. No sé si esto es lo que está buscando: el Informe Revisado Haskell 98 , en la sección 3.3 Curried Applications y Lambda Abstractions , dice:

La aplicación de función está escrita e1 e2 . La aplicación se asocia a la izquierda, por lo que los paréntesis pueden omitirse en (fx) y .