2009-11-30 10 views
13

Tengo un problema con el alcance de Haskell en las definiciones where. Cuando tengo la siguiente función f, donde quiero pasar el x a la función f1 definida localmente sin usarlo explícitamente como parámetro, me sale un error que dice que el tipo de x es incompatible con el de f1, aunque debería ser el mismo:Haskell scoping en definiciones de funciones anidadas utilizando

 
f :: Eq a => a -> [a] 
f x = f1 x 
    where 
     f1 :: Eq a => a -> [a] 
     f1 y = [ x, y ] 

el error es la siguiente:

 
    Couldn't match expected type `a1' against inferred type `a' 
     `a1' is a rigid type variable bound by 
      the type signature for `f1' at test.hs:4:11 
     `a' is a rigid type variable bound by 
      the type signature for `f' at test.hs:1:8 
    In the expression: x 
    In the expression: [x, y] 
    In the definition of `f1': f1 y = [x, y] 
Failed, modules loaded: none. 

Cuando sin embargo pasar el x como un parámetro adicional, como lo hice en el siguiente código con la función g, que funciona bien:

 
g :: Eq a => a -> [a] 
g x = g1 x x 
    where 
     g1 :: Eq a => a -> a -> [a] 
     g1 x y = [ x, y ] 

¿Hay una manera de hacer el tipo a en f compatibles con el tipo a (o a1) en f1?

+1

Aunque es una muy buena práctica actualizar su código con lotes de firmas de tipo, excepto las partes extremadamente triviales, no veo la razón para actualizar las funciones de alcance local con ellas. Es obvio cuál es la firma de tipo, y para funciones triviales como esa, pierdes más en legibilidad de lo que ganas. – Rayne

+1

Bueno, la función real era más compleja, solo la reduje a un simple ejemplo, por lo que mi problema real se aclararía. – poke

Respuesta

11

El problema con su código es la firma local de tipo f1. Se especifica que f1 pueden tomar cualquier tipo

f1 :: Eq a => a -> [a]

A pesar de que esto es una función local, usted ha generalizado esta función para ser capaz de tomar un tipo que no va a existir dentro de f, lo que recibe esta función TIENE que provenir de f, por lo que la firma de tipo no es necesaria.

Simplemente elimine la firma tipo f1.

Editar: Leí mi publicación de nuevo, no está nada claro. a en f1 es un tipo parametrizado que puede tomar cualquier cosa, pero los argumentos que se le pasan ya están vinculados en f. Entonces, esta función solo puede recibir lo que recibe su función padre, la firma de tipo que le está dando rompe esa regla. Espero que sea un poco más claro.

+0

Sí, eso funciona, gracias :) Aunque todavía deja un recelo al no poder escribir explícitamente la función 'f1' (vengo de un lenguaje mucho más estricto), pero bueno, mientras nada se rompa;) – poke

+5

Poke: El problema aquí es que Haskell está siendo más estricto de lo que esperas que sea. Su firma de tipo original para 'f1' especificaba que el argumento podía ser cualquier cosa que perteneciera a la clase de tipo 'Eq', pero el compilador vio correctamente que el argumento pasado a 'f1' en realidad tenía que ser exactamente lo que pasó a 'f'. La eliminación de la firma de tipo para 'f1' no afloja el rigor de tipo. Hacerlo solo le permite al compilador deducir el tipeo por usted. –

13

Dave está justo arriba. Otra forma de pensarlo es que, aunque ambas firmas de tipo se refieren a la variable a, en realidad no es la misma variable de tipo. En la notación Haskell-prime, ambas firmas pueden ser más explícita por escrito como:

forall a . Eq a => a -> [a]

lo que significa que para ambos funciones, se pueden aceptar un argumento del tipo que sea (dentro de la ecuación). Esto obviamente no es el caso aquí. En Haskell 98 estándar, la única opción es renunciar a la firma tipo para f1. Pero GHC (¿y otros?) Son compatibles con lexically scoped type variables. Entonces usted podría escribir

f :: forall a. Eq a => a -> [a] 
f x = f1 x 
    where 
     f1 :: a -> [a] 
     f1 y = [ x, y ] 

y eso funcionaría bien.

Cuestiones relacionadas