Eu coloquei uma restrição no tipo de argumento de uma função em vez de colocar no tipo de função.
Eu pensei que isso daria um erro de sintaxe ou adicionaria mais informações ao tipo da função.
Mas parece que a restrição é completamente ignorada.
{-# 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"
Isso fornece o seguinte erro de tipo:
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"
|
O que colocar uma restrição no tipo de argumento realmente faz?
EDITAR:
Por que isso precisa de RankNTypes
extensão?
Não é necessário se eu remover a (Num a, Ord a) =>
restrição.
Respostas:
Este é um exemplo bastante exótico de subsunção polimórfica, conforme descrito aqui , interagindo com a subsunção de restrição.
Se um tipo
a
subsumeb
,exp :: a
implicaexp :: b
na linguagem da superfície. Um exemplo particular de subsunção é o quef :: forall a. a -> a
implicaf :: Int -> Int
. Além disso, temosn :: Int
implicaçõesn :: c => Int
para qualquerc
restrição.No entanto, no idioma principal, não há subsunção. Todo caso de subsunção no idioma de superfície deve ser traduzido para lambdas e aplicativos explícitos. Além disso,
c => a
simplesmente se tornac -> a
, e o uso de funções restritas é traduzido para a aplicação simples de funções def :: c => a
algunsinst :: c
. Portanto,f :: forall a. a -> a
tornaf @Int :: Int -> Int
-n :: Int
se e torna - se\_ -> n :: c -> Int
.Um caso raramente usado é a regra de subsunção contravariante para funções. O código a seguir é válido:
Isso é traduzido para
Funciona da mesma forma com a subsunção de restrição:
Que é traduzido para
Aproximando-se da pergunta original, se tivermos
como definição principal, seu tipo inferido é
forall a. (Eq a => a) -> Bool
. No entanto, podemos ter qualquer anotação de tipof
incluída no tipo inferido! Então, podemos ter:E GHC ainda está feliz. O código original
é equivalente à seguinte versão um pouco mais clara:
O erro de tipo que você recebe é simplesmente porque
n
é realmente uma função com dois argumentos, um com tipoNum a
e outroOrd a
, e esses dois argumentos são registrosNum
eOrd
métodos. No entanto, como não existem instâncias no escopo na definição, você não pode usá-lon
como um número. A tradução seria convertidan > 10
para(>) inst (n inst) (10 inst)
, whereinst :: Num a
, mas não existeinst
, portanto, não podemos traduzir.Portanto, no corpo do
test
código ainda é verificadon :: (Num a, Ord a) => a)
. No entanto, se retornarmos "Hello" sem usarn
, de maneira semelhante aof
caso anterior , obteremos um tipo inferido que substitui oforall a. a -> String
tipo de anotação. A subsunção é então realizada na saída da tradução substituindo todas as ocorrências den
no corpo detest
por\_ -> n
. Mas comon
não ocorre no corpo, a tradução não faz nada aqui.fonte