O que faz uma restrição no tipo de argumento em vez do tipo de função?

8

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 RankNTypesextensão?
Não é necessário se eu remover a (Num a, Ord a) =>restrição.

happycoder97
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Samuel Liew

Respostas:

7

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 asubsume b, exp :: aimplica exp :: bna linguagem da superfície. Um exemplo particular de subsunção é o que f :: forall a. a -> aimplica f :: Int -> Int. Além disso, temos n :: Intimplicações n :: c => Intpara qualquer crestriçã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 => asimplesmente se torna c -> a, e o uso de funções restritas é traduzido para a aplicação simples de funções de f :: c => aalguns inst :: c. Portanto, f :: forall a. a -> atorna f @Int :: Int -> Int- n :: Intse 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:

f :: (Int -> Int) -> Bool
f _ = True

g :: (forall a. a -> a) -> Bool
g = f

Isso é traduzido para

f :: (Int -> Int) -> Bool
f = \_ -> True

g :: (forall a. a -> a) -> Bool
g = \x -> f (x @Int)

Funciona da mesma forma com a subsunção de restrição:

f :: forall a. (Eq a => a) -> Bool
f _ = True

g :: forall a . a -> Bool
g = f

Que é traduzido para

f :: forall a. (Eq a -> a) -> Bool
f = \_ -> True

g :: forall a . a -> Bool
g = \x -> f (\_ -> x)

Aproximando-se da pergunta original, se tivermos

f (x :: Eq a => a) = True

como definição principal, seu tipo inferido é forall a. (Eq a => a) -> Bool. No entanto, podemos ter qualquer anotação de tipo fincluída no tipo inferido! Então, podemos ter:

f :: forall a. a -> Bool
f (x :: Eq a => a) = True

E GHC ainda está feliz. O código original

test :: a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

é equivalente à seguinte versão um pouco mais clara:

test :: forall a. a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

O erro de tipo que você recebe é simplesmente porque né realmente uma função com dois argumentos, um com tipo Num ae outro Ord a, e esses dois argumentos são registros Nume Ordmétodos. No entanto, como não existem instâncias no escopo na definição, você não pode usá-lo ncomo um número. A tradução seria convertida n > 10para (>) inst (n inst) (10 inst), where inst :: Num a, mas não existe inst, portanto, não podemos traduzir.

Portanto, no corpo do testcódigo ainda é verificado n :: (Num a, Ord a) => a). No entanto, se retornarmos "Hello" sem usar n, de maneira semelhante ao fcaso anterior , obteremos um tipo inferido que substitui o forall a. a -> Stringtipo de anotação. A subsunção é então realizada na saída da tradução substituindo todas as ocorrências de nno corpo de testpor \_ -> n. Mas como nnão ocorre no corpo, a tradução não faz nada aqui.

András Kovács
fonte