140 likes | 262 Views
A Staged Semantics of Overloading (preliminary). Bill Harrison & Tim Sheard. Related Work. Making Ad Hoc Polymorphism Less Ad Hoc, (Wadler & Blott[1989],…) Source to source “dictionary-passing transformation” Dictionary-free Overloading by Partial Evaluation (Jones[1995])
E N D
A Staged Semantics of Overloading (preliminary) Bill Harrison & Tim Sheard
Related Work • Making Ad Hoc Polymorphism Less Ad Hoc, (Wadler & Blott[1989],…) • Source to source “dictionary-passing transformation” • Dictionary-free Overloading by Partial Evaluation (Jones[1995]) • Resolves overloading by inlining methods • Avoids source transformation, but fails for polymorphic recursion • Metacomputation-based Compiler Architecture (Harrison&Kamin[2000]) • Introduces denotational model of staging • Two-monad approach (Static ; Dynam)
Today • Show how techniques in staged semantics apply to overloading • Simple language E: terms of type (Eq a,Num a) =>… • Not claiming to have Haskell nailed, but… • Compelling examples including polymorphic recursion • Methodology: • Annotate some source terms with type information • Namely, “==“, arith., and poly-rec. applications • all (plausibly) derivable from a type derivation • Idea: Given binding for “a”, • First stage resolves overloading statically (if possible) • Second stage may uses type info to update “==“ • No dictionaries: either inlining or runtime update • Three definitions in MetaML: reference, inlining, and runtime updating definitions
Avoiding Dictionary Passing Translation • Try inlining? I.e., for any instance of a in: replace “==“ by a specific equality operation Ex: (\ x -> [x]==[x]) :: Eq a => a -> Bool (\ x -> [x] `eqInt` [x]) :: Int -> Bool advantage: can be accomplished via meaning-preserving process: partial evaluation
Why polymorphic recursion complicates the problem Consider the poly-rec. Haskell function “weird” weird :: Eq a => a -> Bool weird x = x==x && (weird [x]) applied at a applied at [a] At runtime, a poly-rec. function may require “==”s
Outline of Talk • Source language E • Reference semantics: usual monadic def’n of E. mng :: E -> <Dynam Value> • Inlining semantics inline :: E -> S <Dynam Value> • Runtime updatable semantics pr :: E -> S <Dynam Value>
Source Language E datatype Type = Integer | Real | Bool | Arrow of (Type * Type) | List of Type | Pair of (Type * Type) | TV of Name ; datatype BinOp = Add | Mul | Sub | And; datatype E = lambda of Name*E | app of E*E | var of Name | ite of (E * E * E) | letrec of (Name * E * E) | nilexp | cons of (E * E) | pair of (E * E) | equal of (E * E * Type) | binop of (BinOp * E * E * Type) | const of (Name * Type) | prapp of (E * E * Type); Interesting stuff involves overloaded terms
Reference semantics • Uses “dynamic” monad D • Non-proper morphisms: datatype 'a D = D of (Env -> 'a) and Env = Env of (Name -> Value D) and Value = Z of int | R of real | B of bool | L of (Value list) | P of (Value * Value) | Fun of (Value D -> Value D) | EqOp of (Value -> Value -> Value); rdEnv :: D Env inEnv :: Env -> D a -> D a xEnv :: Env -> Name -> D a -> D a
Reference semantics (cont’d) • (mng e) defined mostly in terms of “standard” operations: • But, there’s no overloading: • Ex: (mng (lambda ("x",(var "x"))) produces: fun mng e = case e of (var x) => <mkread ~(lift x)> where fun mkread x = Do d { (Env rho) <- rdEnv ; (rho x) }; (const (tag,_)) => case tag of "zero" => <Return d (Z 0)> | "one" => <Return d (Z 1)> <mkfun "x" (mkread "x")> : <Value D>
The “static” monad S • S includes type environment and “Maybe” effects • Type annotations may have variables • Resolving overloading may fail for some “a” • Improper morphisms • Idea: using binding for “a”, • Use Type annotations in terms to calculate specific operations • And inline them in the result rdTypeEnv :: S TypeEnv inTypeEnv :: TypeEnv -> S a -> S a errMsg :: String -> S a
Inlining semantics inline :: E -> S <D Value> • Stage mng by including a static S phase • (inline e) works in S to produce D-computation • Unannotated definitions are similar: • Overloaded terms: fun inline e = case e of (var x) => Return s <mkread ~(lift x)> (equal (e1,e2,t)) => Do s { phi1 <- inline e1 ; phi2 <- inline e2 ; equality <- calcEqOp t ; Return s <mkeq ~equality ~phi1 ~phi2> } Where ~equality will be an (monomorphic) equality operator like: val eqInteger = fn (Z i) => fn (Z j) => B (i=j);
Example For function foo: foo :: Eq a => a -> Bool foo x = ([x]==[x]) Produces the following if “a” is Integer: -| inlineshow foo Integer; val it = <mkfun "x" (mkeq (eqList eqInteger) (mkread "x") (mkread "x"))> : <Value D>
Example for third definition Third semantic definition: pr :: E -> S <D Value> Recall: weird :: Eq a => a -> Bool weird x = x==x && (weird [x]) Consider this term (here using Haskell concrete syntax) let weird x = x==x && (weird [x]) in (weird 3)
Dynamic Part of “weird” <Do d { rhoInit <- rdEnv --- initial eq. oper. ; inEnv (xEnv rhoInit "==" (Return d (EqOp eqInteger))) (mkletrec "weird" --- letrec weird =… (mkfun "x" (mkbinop andBool (deltaEqOp (TV "a") --- x==x (Do d { Env b <- rdEnv ; EqOp c <- b "==" ; mkeq c (mkread "x") (mkread "x") })) (deltaEqOp (List (TV "a")) --- weird [x] (mkapp (mkread "weird") (mkcons (mkread "x") mknil))))) (mkapp (mkread "weird") --- in (weird 3) (mkbinop addInt (Return d Z 1) (mkbinop addInt (Return d Z 1) (Return d Z 1))))) }> : <Value D>