# Let should not be generalized Dimitrios Vytiniotis, Simon Peyton Jones Microsoft Research, Cambridge TLDI’1 0, Madrid, January 2010 Tom Schrijvers K.U.

Post on 14-Jan-2016

212 views

Embed Size (px)

TRANSCRIPT

<p>Implementing type inference for first-class polymorphism</p>
<p>let should not be generalizedDimitrios Vytiniotis, Simon Peyton Jones Microsoft Research, CambridgeTLDI10, Madrid, January 2010Tom SchrijversK.U. LeuvenExtending ML type inference with 1Advanced types Generalized Algebraic Datatypes (GADTs) [Cheney & Hinze, Xi, Peyton Jones et al., Pottier & Simonet, Pottier & Regis-Gianas,]Open Type Families [Schrijvers et al., ICFP 2007] types indexed by some constraint domain [e.g. Kennedys types indexed by Units of Measure, ESOP94]</p>
<p>Advanced forms of constraintsType equalities with type families, type class constraintsImplication constraints that arise because of pattern matching[Pottier & Regis-Gianas, Sulzmann et al.]</p>
<p>A question: How should we be generalizing let-bound definitions?</p>
<p>Why is this question relevant? 2Type system decisions (as let-generalization) affectImplementability of type inference & checking Complexity of implementationEfficiency of implementationProgrammabilityPredictability of type checkingBackwards compatibility (lots of Haskell 98 code!) </p>
<p>GOAL: Support advanced forms of types and constraints mentionedPerform well in (1 6)</p>
<p>Generalized Algebraic Datatypes 3 </p>
<p>(a ~ Int) => (Int ~ a)(a ~ Bool) => (Bool ~ a)GADT data constructors introduce constraints Pattern matching creates implication constraints That a solver must discharge </p>
<p>data R a where Rint :: (a ~ Int) => R a Rbool :: (a ~ Bool) => R a</p>
<p>create :: R a -> a create Rint = 42 create Rbool = False </p>
<p>Constraint introduced by Rint42 : Int Expected type: aGADTs and Generalization 4 </p>
<p>Observation:(flop 43) and (flop False) can potentially reach first or second branch No DEAD code in the example * data R a where Rint :: (a ~ Int) => R a Rbool :: (a ~ Bool) => R a</p>
<p>mkR :: a -> R a </p>
<p>flop x = let g () = not x -- not :: Bool -> Bool in case (mkR x) of Rbool -> g () Rint -> True x : ~ BoolmkR x : R ~ Bool => ? ~ Int => true GADTs and Generalization 5 </p>
<p>What is the type of g? If spec does not allow quantification over equalities () -> BoolIf spec does allow quantification over equalities () => Bool or ( ~ Bool) => () -> Bool data R a where Rint :: (a ~ Int) => R a Rbool :: (a ~ Bool) => R a</p>
<p>mkR :: a -> R a </p>
<p>flop x = let g () = not x -- not :: Bool -> Bool in case (mkR x) of Rbool -> g () Rint -> True x : ~ BoolmkR x : R ~ Bool => ? ~ Int => true Quantifying over equalities 6 </p>
<p>data R a where Rint :: (a ~ Int) => R a Rbool :: (a ~ Bool) => R a</p>
<p>mkR :: a -> R a </p>
<p>flop x = let g () = not x -- not :: Bool -> Bool in case (mkR x) of Rbool -> g () Rint -> True Option 1I = Boolg :: () -> Bool</p>
<p> Option I g :: ~ Bool => () -> Bool g :: () -> BoolSecond branch rejected (Bool =/= Int) or typeable as unreachableHence, for GADTs: 7We want to unify away as much as possible (type of x) For simpler typesFor support for some eager solvingFor shorter constraints </p>
<p>But we cant unify variables bound in the environment!</p>
<p>if the type system allows quantification over equalities then we must defer a lot of silly unifications as constraints</p>
<p>Open type families8Programmers declare type-level computations</p>
<p>And give axiom schemes for themforall (b).G b Int ~ b In GHC the axiom scheme definitions are openIf G Bool ~ Bool we must not conclude that ~ Int[think of another consistent axiom G Bool Char ~ Bool] type family G a b type instance G b Int = b</p>
<p>Quantifying restricted constraints?9Ok, if equalities are problematic quantify over:Class constraints: Eq Type family constraints: F ~ Int</p>
<p>Problematic: We want to rewrite as much as possibleBut we must not rewrite too much. Rather delicate!type family G a b type instance G b Int = b</p>
<p>flop x = let test = ... in ... x :: Yielding constraint: G Int ~ Int Yielding type: -> G Int ~ & G Int ~ Int which gives ~ IntQuantifying over only class constraints?10Problematic: class constraints may include superclasses:</p>
<p>Even if not, its hard to give a complete specification</p>
<p>Left-to-right: REJECT (cant discharge constraint)And its not right to defer unsolvable (forall b. F b ~ Int) Right-to-left: ACCEPT Rest of typing problem determines ~ Int and triggers axiom!Class (a ~ b) => Eq a btype instance F Int b = b </p>
<p>let f x = (let h y = (yielding F ~ Int) in 42, x + 42) x :: y :: Let generalization not a new problem really11A. Kennedy knew about it when I was 14! [LIX RR/96/09] </p>
<p>Kennedy used a clever domain-specific solutionConstraint equivalent to: v ~ /uype: forall u. Num u -> Num (/u)div :: forall u v. Num (u * v) -> Num u -> Num vweight :: Num kgtime :: Num sec</p>
<p>flop x = let y = div x in (y weight, y time)</p>
<p>x::Yielding constraint: ~ (u * v)Yielding type: u -> vSolving gives = u * v and g becomes monomorphic!Which IS polymorphic The proposal12The specification and implementation costs for generalizing local let-bound expressions are becoming high:</p>
<p>Do NOT Generalize Local Let-Bound Expressions</p>
<p>Top-level ones do not contribute to the problems[No environment to interact with]Local but annotated let definitions can be polymorphic</p>
<p>Most Haskell 98 programs actually do not use local let polymorphism (though arguably code refactoring tools may). Results performed in Hackage reported in paper</p>
<p>Also in paper13Combining Let Should Not Be Generalized with the OutsideIn [ICFP09] strategy for solving implication constraints leads to LHM(X):</p>
<p>HM(X) with Local assumptions from pattern matching</p>
<p>Type system parameterized over constraint domain X Inference algorithm parameterized over X solverSoundness result provided X solver assumptions </p>
<p> towards pluggable type systems + type inference</p>
<p>A challenge for type system designers14Solvers for type class and family instance constraints are inherently weak (by design) under an open world assumption</p>
<p>Constraint arising: F ~ IntInstance declaration: type instance F Int ~ IntQuestion: What is ? </p>
<p>Type system may guess = Int, but algorithm cant [or shouldnt] Challenge: Find a declarative specification that rejects ambiguity</p>
<p>A first step: ambiguous constraints15Constraint C is unambiguously solvable in a top-level theory T: usolv(T,C) iff T shows (C) and T & C shows ()</p>
<p>The constraint must be solvable by a substitution derivable by massaging the constraint: usolv(F Int ~ Int, F ~ Int) because: (F Int ~ Int & F ~ Int) =/=> ( ~ Int) Similar definition by Sulzmann & Stuckey [TOPLAS2005], also recent related work by Camarao et al. </p>
<p>Conclusions and directions16The cost of implicit local generalization is high, we should find alternatives or not generalize implicitly</p>
<p>Directions and ongoing work: </p>
<p>A full GHC implementation that supports Haskell type classes, GADTs, type functions, and first-class polymorphism [journal submission soon]</p>
<p>A declarative specification that deals with ambiguity is open</p>
<p>17Thank you for your attention</p>

Recommended

View more >