Typinferenz + Überladung

9

Ich suche nach einem Typinferenzalgorithmus für eine Sprache, die ich entwickle, aber ich konnte keinen finden, der meinen Anforderungen entspricht, da dies normalerweise entweder der Fall ist:

  • à la Haskell, mit Polymorphismus, aber ohne Ad-hoc-Überlastung
  • à la C ++ (Auto), bei dem Sie eine Ad-hoc-Überladung haben, die Funktionen jedoch monomorph sind

Insbesondere ist mein Typsystem (vereinfachend) (ich verwende die Haskellish-Syntax, aber dies ist sprachunabhängig):

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

Und ich habe einen Operator *, der einige Überlastungen hat:

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

Usw...

Und ich möchte mögliche Typen für ableiten

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

Der erste ist Intder zweite Matrix Int.

Beispiel (das funktioniert nicht):

{-# 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
quelle
3
Dies scheint nicht die Kriterien für CS Theory Stack Exchange zu erfüllen, aber es sieht so aus, als würden Sie nach einer mathematischeren oder theoretischeren Antwort suchen. Ich denke, dass Informatik dafür angemessen sein könnte. Da Sie um einen Umzug gebeten haben, um bessere Antworten zu erhalten, sende ich ihn an den Ort, an dem er wahrscheinlich gut ankommt.
Thomas Owens

Antworten:

9

Ich würde vorschlagen, sich Geoffrey Seward Smiths Dissertation anzuschauen

Wie Sie wahrscheinlich bereits wissen, funktionieren die gängigen Typinferenzalgorithmen so, dass sie den Syntaxbaum durchlaufen und für jeden Unterausdruck eine Typbeschränkung generieren. Dann nehmen sie diese Einschränkungen, nehmen eine Verbindung zwischen ihnen an und lösen sie (normalerweise auf der Suche nach einer allgemeinsten Lösung).

Wenn Sie auch überladen sind, generieren Sie bei der Analyse eines überladenen Operators mehrere Typbeschränkungen anstelle einer und nehmen eine Disjunktion zwischen ihnen an, wenn die Überladung begrenzt ist. Weil Sie im Wesentlichen sagen, dass der Operator entweder diesen oder jenen oder diesen Typ haben kann. Wenn er unbegrenzt ist, muss auf eine universelle Quantifizierung zurückgegriffen werden, genau wie bei polymorphen Typen, aber mit zusätzlichen Einschränkungen, die den tatsächlichen einschränken Überladungsarten. Das Papier, auf das ich verweise, behandelt diese Themen ausführlicher.

bellpeace
quelle
Vielen Dank, das ist die Antwort, nach der ich gesucht habe
miniBill
7

Merkwürdigerweise Haskell selbst ist perfekt fast der Lage , Ihr Beispiel; Hindley-Milner ist völlig in Ordnung mit Überladung, solange es gut begrenzt ist:

{-# 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

Sie sind fertig! Nun, außer dass Sie eine Standardeinstellung benötigen. Wenn Ihre Sprache die Standardeinstellung der TimesKlasse Int(und dann Double) zulässt , funktionieren Ihre Beispiele genau wie angegeben. Die andere Art und Weise zu fixieren, ist natürlich nicht automatisch zu fördern , Intum Double, oder es nur tun , wenn sie unmittelbar notwendig ist , und zu verwenden IntLiterale als Intnur (wieder nur zu fördern , wenn sie unmittelbar erforderlich); Diese Lösung wird auch funktionieren.

Pthariens Flamme
quelle
2
Vielen Dank! (obwohl die Anzahl der Erweiterungen über 9k ist)
MiniBill
1
Funktioniert nicht ideone.com/s9rSi7
miniBill
1
Es ist kein Standardproblem
miniBill
1
Oh, sorry, ich habe dich damals falsch verstanden. Nun, ich möchte nicht in Verzug
geraten
2
Könnten Sie versuchen, ein kompilierbares Beispiel zu erstellen?
MiniBill