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 Int
der 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
type-theory
type-inference
miniBill
quelle
quelle
Antworten:
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.
quelle
Merkwürdigerweise Haskell selbst ist
perfektfast der Lage , Ihr Beispiel; Hindley-Milner ist völlig in Ordnung mit Überladung, solange es gut begrenzt ist:Sie sind fertig! Nun, außer dass Sie eine Standardeinstellung benötigen. Wenn Ihre Sprache die Standardeinstellung der
Times
KlasseInt
(und dannDouble
) zulässt , funktionieren Ihre Beispiele genau wie angegeben. Die andere Art und Weise zu fixieren, ist natürlich nicht automatisch zu fördern ,Int
umDouble
, oder es nur tun , wenn sie unmittelbar notwendig ist , und zu verwendenInt
Literale alsInt
nur (wieder nur zu fördern , wenn sie unmittelbar erforderlich); Diese Lösung wird auch funktionieren.quelle