2012-05-26 18 views
19

Duplicar posible:
Why is such a function definition not allowed in haskell?Haskell "Aplicar"?

yo soy un recién llegado al mundo de Haskell, migrando el relevo de Lisp. Estoy tratando de adaptarme a la cosmovisión fundamentalmente diferente de Haskell, y una de las muchas cosas que encuentro nuevas y emocionantes es el sistema de tipos. Siendo un Lisper, pensé que intentaría implementar en Haskell una función que es muy importante en el mundo de Lisp: apply. Para aquellos que no saben, apply toma una función y una lista de argumentos, e invoca la función en esos argumentos. En el esquema, (apply + '(1 2 3)) es el mismo que invocar (+ 1 2 3), y devuelve 6.

código

Mi Haskell es como la siguiente:

apply x [] = x 
apply f (x:xs) = apply (f x) xs 

Pero Haskell se queja:

ERROR line 2 - Type error in function binding 
*** Term   : apply 
*** Type   : (b -> a) -> [b] -> a 
*** Does not match : a -> [b] -> a 
*** Because  : unification would give infinite type 

Y creo que yo entender porqué. El tipo de Apply debe ser diferente según la longitud de la lista que se le dé. Dada una lista de, digamos, 3 elementos, el tipo de solicitud debería ser: (a -> a -> a -> b) -> [a] -> b, pero dada una lista de 6 elementos, el tipo de solicitud debería ser: (a -> a -> a -> a -> a -> a -> b) -> [a] -> b.

yo probamos este trabajo en torno horrendo:

data FnOrDat a b = Dat b | Fn (a -> FnOrDat a b) 

apply :: (FnOrDat a b) -> [a] -> (FnOrDat a b) 
apply x [] = x 
apply (Fn f) (x:xs) = apply (f x) xs 
apply (Dat _) _ = error "Cannot apply something which is not a function!" 

add a = Fn (\b -> Dat (a + b)) 

main = putStrLn $ show $ x where Dat x = apply (Fn add) [5,1] 

Esto funciona, pero apenas cuenta como una función apply, porque no puedo pasar apply una función normal, tengo que usar uno que ha sido escrito específicamente para usar mi (incómoda) abstracción FnOrDat. Si quisiera escribir una función que agregara cuatro números, necesitaría escribir

add4 a = Fn (\b -> Fn (\c -> Fn (\d -> Dat (a + b + c + d)))) 

Ew.

Entonces, ¿me falta algo, o estoy pidiendo un apply de propósito general básicamente como pedir una función que pueda manipular una tupla de longitud arbitraria? ¿Tiene sentido el apply en la cosmovisión de tipo estático de Haskell?

Respuesta

11

apply no es muy útil en Haskell, ya que no se puede asignar un tipo a la función. Como puede ver en su FnOrDat, básicamente está integrando un lenguaje Lisp en Haskell como EDSL para forzar el paso.

pidiendo una de propósito general se aplican básicamente como pedir una función que puede manipular un tupla de longitud arbitraria?

Exactamente. Puede encontrar instancias de clase de tipo para ciertas combinaciones útiles de tipos, pero simplemente no es realmente necesario o no se usa para una variable general apply.


Como nota al margen, se debe considerar la actualización a GHC y la Haskell Platform, en lugar del sistema abrazos obsoleta, ya que se está perdiendo en la mayoría de las bibliotecas, herramientas y características del lenguaje desarrollado en los últimos 10 años .

+2

Gracias! En realidad, uso GHC en mi computadora "principal". Estoy escribiendo esto en otro lugar, así que acabo de usar codepad.org para probar mis fragmentos de código. Supongo que usan abrazos? – Ord

+0

@Ord usan abrazos, con el indicador '-98'. Ver http://codepad.org/about –

2

A pesar de la explicación de Don, foldl1 (+) realmente agregaría todos los elementos de una lista. Por lo tanto, se podría decir que la familia de funciones fold se acerca bastante al apply como lo describe el OP.

+1

Sí, para ciertos tipos de funciones, supongo. Pero si tuviera una función como 'discriminante a b c = b * b - 4 * a * c', en Lisp podría decir' (aplicar discriminante '(2 3 4)) '. No puedes hacer esto con fold. – Ord

+7

Por otro lado, no sé por qué aplicar alguna vez se usaría así. En Lisp, generalmente usas apply cuando tienes una función que acepta cualquier cantidad de argumentos, y este concepto no se traslada a Haskell. – Ord

+0

Bueno, sí, pero luego 'apply3 f (a: b: c: _) = f a b c' es un trazador de líneas. – Ingo

0

... una función que es muy importante en el mundo de Lisp: aplicar. Para aquellos que no sepan, aplicar toma una función y una lista de argumentos y invoca la función en esos argumentos. En Scheme, (aplicar + '(1 2 3)) es lo mismo que invocar (+ 1 2 3), y devuelve . ...

Esto es bastante simple:

foldr (+) 0 [1,2,3] 
foldr1 (+) [1,2,3] 

resultados en 6.

Para aplicar una función a cada elemento de una lista:

map f list 

por ejemplo,

map (2*) [1,2,3] 

resultados en [2,4,6]

Es esto lo que busca?

+2

No del todo. Si define 'discriminante a b c = b * b - 4 * a * c', en Lisp podría decir' (aplicar discriminante '(2 3 4)) '. Otro ejemplo (más útil): en Lisp, la función de mapa puede tomar cualquier número de argumentos de lista. No solo puedo decir '(map square '(1 2 3 4)) ->' (1 4 9 16)', pero puedo decir '(map + '(1 2 3)' (4 5 6)) - > '(5 7 9) '(que es como zipWith), o' (map *' (1 2 3) '(3 2 1)' (0 2 0)) -> '(0 8 0) '. Si tengo una lista de listas, x, podría decir '(aplicar mapa + x)' para obtener la suma a término de las listas. No puedes hacer ninguna de estas cosas con fold. – Ord