Inferência de tipo + sobrecarga

9

Estou procurando um algoritmo de inferência de tipo para uma linguagem que estou desenvolvendo, mas não consegui encontrar uma que atenda às minhas necessidades porque elas geralmente são:

  • à Haskell, com polimorfismo, mas sem sobrecarga ad-hoc
  • à C ++ (automático) no qual você tem sobrecarga ad-hoc, mas as funções são monomórficas

Em particular, meu sistema de tipos é (simplificador) (estou usando a sintaxe Haskellish, mas isso é independente do idioma):

data Type = Int | Double | Matrix Type | Function Type Type

E eu tenho um operador * que tem algumas sobrecargas:

Int -> Int -> Int
(Function Int Int) -> Int -> Int
Int -> (Function Int Int) -> (Function Int Int)
(Function Int Int) -> (Function Int Int) -> (Function Int Int)
Int -> Matrix Int -> Matrix Int
Matrix Int -> Matrix Int -> Matrix Int
(Function (Matrix Int) (Matrix Int)) -> Matrix Int -> Matrix Int

Etc ...

E quero inferir possíveis tipos de

(2*(x => 2*x))*6
(2*(x => 2*x))*{{1,2},{3,4}}

O primeiro é Into segundo Matrix Int.

Exemplo (que não funciona):

{-# LANGUAGE OverlappingInstances, MultiParamTypeClasses,
  FunctionalDependencies, FlexibleContexts,
  FlexibleInstances, UndecidableInstances #-}

import qualified Prelude
import Prelude hiding ((+), (*))
import qualified Prelude

newtype WInt = WInt { unwrap :: Int }

liftW f a b = WInt $ f (unwrap a) (unwrap b)

class Times a b c | a b -> c where
(*) :: a -> b -> c

instance Times WInt WInt WInt where
(*) = liftW (Prelude.*)

instance (Times a b c) => Times a (r -> b) (r -> c) where
x * g = \v -> x * g v

instance Times (a -> b) a b where
f * y = f y

two = WInt 2
six = WInt 6

test :: WInt
test = (two*(\x -> two*x))*six

main = undefined
miniBill
fonte
3
Isso não parece atender aos critérios do CS Theory Stack Exchange, mas parece que você está procurando uma resposta mais matemática ou teórica. Eu acho que a Ciência da Computação pode ser apropriada para isso. Como você solicitou uma mudança para obter melhores respostas, eu a enviarei para onde é provável que seja bem recebida.
Thomas Owens

Respostas:

9

Eu sugeriria olhar dissertação de Geoffrey Seward Smith

Como você provavelmente já sabe, a maneira como os algoritmos comuns de inferência de tipo funcionam é que eles atravessam a árvore de sintaxe e, para cada subexpressão, geram uma restrição de tipo. Então, eles pegam essas restrições, assumem a conjunção entre elas e as resolvem (geralmente procurando uma solução mais geral).

Quando você também tem sobrecarga, ao analisar um operador sobrecarregado, gera várias restrições de tipo, em vez de uma, e assume a disjunção entre elas, se a sobrecarga estiver limitada. Porque você está essencialmente dizendo que o operador pode ter `` este ou este ou aquele tipo ''. Se não tem limites, é preciso recorrer à quantificação universal, assim como nos tipos polimórficos, mas com restrições adicionais que restringem o real tipos de sobrecarga.O artigo que eu refiro aborda esses tópicos com mais profundidade.

bellpeace
fonte
Muito obrigado, esta é a resposta que eu estava procurando
miniBill
7

Curiosamente, o próprio Haskell é perfeitamente quase capaz do seu exemplo; A Hindley-Milner está totalmente bem com a sobrecarga, desde que bem limitada:

{-# LANGUAGE OverlappingInstances, MultiParamTypeClasses,
             FunctionalDependencies, FlexibleContexts,
             FlexibleInstances #-}
import Prelude hiding ((*))

class Times a b c | a b -> c where
    (*) :: a -> b -> c

instance Times Int Int Int where
    (*) = (Prelude.*)

instance Times Double Double Double where
    (*) = (Prelude.*)

instance (Times a b c) => Times (r -> a) (r -> b) (r -> c) where
    f * g = \v -> f v * g v

instance (Times a b c) => Times a (r -> b) (r -> c) where
    x * g = \v -> x * g v

instance (Times a b c) => Times (r -> a) b (r -> c) where
    f * y = \v -> f v * y

type Matrix a = (Int, Int) -> a

Você Terminou! Bem, exceto que você precisa deixar o padrão. Se o seu idioma permitir o padrão da Timesclasse Int(e depois Double), seus exemplos funcionarão exatamente como indicado. A outra maneira de corrigi-lo, é claro, é a de não promover automaticamente Inta Double, ou para fazê-lo apenas quando imediatamente necessário, e para uso Intliterais como Intúnica (de novo, promovendo somente quando imediatamente necessário); esta solução também funcionará.

Chama de Ptharien
fonte
2
Muito obrigado! (embora o número de extensões seja superior a 9k)
miniBill
11
Não funciona ideone.com/s9rSi7
miniBill
11
não é um problema inadimplente: ideone.com/LrfEjX
miniBill
11
Oh, desculpe, eu te entendi mal. Bem, eu não quero inadimplente (eu acho) ..
miniBill
2
Você poderia tentar produzir um exemplo que compila?
MinBill