Pongo una restricción en el tipo de argumento de una función en lugar de poner el tipo de la función.
Pensé que esto daría un error de sintaxis o agregaría más información al tipo de función.
Pero parece que la restricción se ignora por completo.
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
test :: a -> String
test (n :: (Num a, Ord a) => a) =
if n > 10 then "Hello"
else "World"
main = print "Hello World"
Esto da el siguiente tipo de error:
Test3.hs:6:8: error:
• No instance for (Num a) arising from a use of ‘n’
Possible fix:
add (Num a) to the context of
the type signature for:
test :: forall a. a -> String
• In the first argument of ‘(>)’, namely ‘n’
In the expression: n > 10
In the expression: if n > 10 then "Hello" else "World"
|
6 | if n > 10 then "Hello"
| ^
Test3.hs:6:8: error:
• No instance for (Ord a) arising from a use of ‘>’
Possible fix:
add (Ord a) to the context of
the type signature for:
test :: forall a. a -> String
• In the expression: n > 10
In the expression: if n > 10 then "Hello" else "World"
In an equation for ‘test’:
test (n :: (Num a, Ord a) => a)
= if n > 10 then "Hello" else "World"
|
6 | if n > 10 then "Hello"
|
¿Qué significa realmente poner una restricción en el tipo de argumento?
EDITAR:
¿Por qué esto necesita RankNTypes
extensión?
No es necesario si elimino la (Num a, Ord a) =>
restricción.
Respuestas:
Esta es una instancia bastante exótica de subsunción polimórfica, como se describe aquí , que interactúa con la subsunción de restricción.
Si un tipo
a
subsumeb
, entoncesexp :: a
implicaexp :: b
en el lenguaje superficial. Un ejemplo particular de subsunción es quef :: forall a. a -> a
implicaf :: Int -> Int
. Además, tenemosn :: Int
implican :: c => Int
para cualquierc
restricción.Sin embargo, en el lenguaje central no hay subsunción en absoluto. Cada caso de subsunción en el lenguaje de superficie debe traducirse a lambdas y aplicaciones explícitas. Además,
c => a
simplemente se conviertec -> a
, y el uso de funciones restringidas se traduce en la aplicación de funciones simplesf :: c => a
para algunosinst :: c
. Por lo tanto, sef :: forall a. a -> a
vuelvef @Int :: Int -> Int
y sen :: Int
vuelve\_ -> n :: c -> Int
.Un caso raramente utilizado es la regla de subsunción contravariante para funciones. El siguiente es un código válido:
Esto se traduce a
Funciona de manera similar con la subsunción de restricción:
Que se traduce a
Acercándonos a la pregunta original, si tenemos
como definición superior, su tipo inferido es
forall a. (Eq a => a) -> Bool
. Sin embargo, podemos tener cualquier tipo de anotación para laf
cual se incluye el tipo inferido. Entonces podemos tener:Y GHC sigue feliz. El código original
es equivalente a la siguiente versión ligeramente más clara:
El error de tipo, que se obtiene es simplemente porque
n
es realmente una función con dos argumentos, uno tiene el tipoNum a
y el otroOrd a
, y ambos de estos argumentos son los registros que contienenNum
yOrd
métodos. Sin embargo, dado que no hay tales instancias en el alcance de la definición, no puede usarlasn
como un número. La traducción se convertirían > 10
a(>) inst (n inst) (10 inst)
, dondeinst :: Num a
, pero no existeinst
, por lo que no podemos traducir.Por lo tanto, en el cuerpo del
test
código todavía se verifica conn :: (Num a, Ord a) => a)
. Sin embargo, si solo devolvemos "Hola" sin usarn
, de manera similar alf
caso anterior , obtenemos un tipo inferido que subsume elforall a. a -> String
tipo de anotación. La subsunción se realiza luego en la salida de traducción reemplazando cada aparición den
en el cuerpo detest
con\_ -> n
. Pero comon
no ocurre en el cuerpo, la traducción no hace nada aquí.fuente