Pregunta Haskell: ¿Las variables de tipo en las cláusulas "where" en el mismo espacio de nombres con sus padres?


En el siguiente fragmento (he resumido todas las otras partes triviales)

data T s = T (s -> s)

foo :: T s -> s -> s
foo (T f) x = bar x where
    bar :: s -> s
    bar a = f a

Recibí un error de seguimiento

Couldn't match expected type `s1' with actual type `s'
  `s1' is a rigid type variable bound by
       the type signature for bar :: s1 -> s1 at /tmp/test.hs:5:12
  `s' is a rigid type variable bound by
      the type signature for foo :: T s -> s -> s at /tmp/test.hs:3:8
In the return type of a call of `f'
In the expression: f a
In an equation for `bar': bar a = f a

Mi conjetura fue que las variables de tipo en bar's firma no comparte el espacio de nombres con foo, por lo que el compilador no puede inferir que los dos s realmente significa el mismo tipo.

Luego encontré esta página Variables de tipo de ámbito, lo que sugiere que puedo usar {-# LANGUAGE ScopedTypeVariables #-}, lo cual no ayudó. Tengo el mismo error.


5
2017-10-11 15:20


origen


Respuestas:


¿Están las variables de tipo en las cláusulas de "dónde" en el mismo espacio de nombres con sus padres?

No*. Esto se vuelve un poco más fácil si piensas en foo :: s -> s en términos de foo :: forall s. s -> s. Después de todo, una variable de tipo indica que la función funciona para cualquier tipo s. Agreguemos cuantificaciones explícitas a su código:

{-# LANGUAGE ExplicitForAll #-}

data T s = T (s -> s)

foo :: forall s. T s -> s -> s
foo (T f) x = bar x where
    bar :: forall s. s -> s
    bar a = f a

Como puedes ver, hay dos forall s. ahí. Pero el de bar Está Mal. Después de todo, no puedes elegir alguna  s allí, pero el ya utilizado en s. Esto se puede hacer habilitando ScopedTypeVariables:

{-# LANGUAGE ScopedTypeVariables #-}

data T s = T (s -> s)

--     vvvvvvvv  explicit here
foo :: forall s. T s -> s -> s
foo (T f) x = bar x where
    --     vvvvvv  same as above here
    bar :: s -> s
    bar a = f a

Sin embargo, hay algunos trucos para deshacerse de ScopedTypeVariables. Por ejemplo lo siguiente en este caso:

data T s = T (s -> s)

foo :: T s -> s -> s
foo (T f) x = (bar `asTypeOf` idType x) x where

    bar a = f a

    idType :: a -> a -> a
    idType a _ = a

-- For completion, definition and type of 'asTypeOf'
-- asTypeOf :: a -> a -> a
-- asTypeOf x _ = x

Para cualquier x :: s el termino idType x tiene tipo s -> sy asTypeOf hace cumplir ambos para tener el mismo tipo.

Dependiendo de su código real, algo como esto podría ser más o menos factible.


* Bueno, en este caso, ya que es posible usar ScopedTypeVariables, ver parte posterior de la respuesta.


7
2017-10-11 15:34



ScopedTypeVariables son de hecho la solución, pero hay un requisito adicional para usarlas: debe poner explícitamente foralls en las firmas de tipo que declaran las variables que desea incluir en el alcance, como esto:

foo :: forall s. T s -> s -> s

Esto es así para que el significado de las firmas en el código no dependa de si la extensión está habilitada o no.


5
2017-10-11 15:25



Preguntas populares