Simon PJ Feb 2012 - ETH Zlaser.inf.ethz.ch/2012/slides/PeytonJones/Deferring type errors.pdf ·...

18
Simon PJ Feb 2012

Transcript of Simon PJ Feb 2012 - ETH Zlaser.inf.ethz.ch/2012/slides/PeytonJones/Deferring type errors.pdf ·...

  • Simon PJ

    Feb 2012

  • The rise of dynamic languages

    “The type errors are getting in my way”

    Feedback to programmer Static: type system

    Dynamic: run tests

    “Programmer is denied dynamic feedback in the periods when the program is not globally type correct” [DuctileJ, ICSE’11]

  • Underlying problem: forces programmer to fix all type errors before running any code.

    Goal: Damn the torpedos

    Compile even type-incorrect programs to executable code, without losing type soundness

  • Not just the command line: can load modules with type errors --- and run them

    bash$ ghci –fdefer-type-errors

    ghci> let foo = (True, ‘a’ && False)

    Warning: can’t match Char with Bool

    gici> fst foo

    True

    ghci> snd foo

    Error: can’t match Char with Bool

  • DuctileJ uses reflection

    Our implementation has zero runtime cost

    (But we are a little bit more eager about raising a runtime type error than they are.)

  • Just a hack? No! A thing of beauty?

    Haskell program

    System FC

    Typecheck and desugar

  • 3+(4::Int)

    (+) d7 3 4 d7 : Num Int

    Type checker

    Constraints Elaborated program (mentioning constraint variables)

  • let d7:Num Int = dNumInt

    (+) d7 3 4 d7 : Num Int

    Constraints Elaborated program (mentioning constraint variables)

    Solve

    Constraint solver creates a value binding giving evidence for each constraint

  • \x. x && False

    \(x:) (x c7) && False c7 : ~ Bool

    Haskell term

    Constraints Elaborated program (mentioning constraint variables)

  • let = Bool let c7: ~Bool = refl Bool

    \(x:). (x c7) && False c7 : ~ Bool

    Constraints Elaborated program (mentioning constraint variables)

    Solve

    Solver also solves for unknown types

    Equality constraints have evidence

    A cast (e1 c) converts a term from one type to another

  • (True, ‘a’ && False)

    (True, (‘a’ c7) && False) c7 : Int ~ Bool

    Haskell term

    Constraints Elaborated program (mentioning constraint variables)

  • let c7: Int~Bool = error “Can’t match ...”

    (True, (‘a’ c7) && False) c7 : Int ~ Bool

    Constraints Elaborated program (mentioning constraint variables)

    Solve

    Use lazily evaluated evidence

    Cast evaluates its evidence

    Error triggered when (and only when) ‘a’ must have type Bool

  • let c7: Int~Bool = error “Can’t match ...”

    in (True, (‘a’ c7) && False)

    The intermediate language is System FC

    Strongly typed like System F

    So even these type-incorrect programs enjoy the type soundness property

    GHC does carry all these coercions throughout; and checks them with –dcore-lint

  • But is this efficient?

    How do you make it efficient enough?

    ML typechecking has zero runtime cost; so anything involving these casts and coercions looks inefficient, doesn’t it?

    EFFICIENCY?

  • Remember: cast evaluates its coercion argument

    Think of (refl Bool) as a value, that does not need to be evaluated by cast

    Moreover, the only thing you need about this value is to have it; once it is evaluated you don’t need anything else

    Sounds rather ad-hoc

    let c7: Bool~Bool = refl Bool in (x c7) && False)

  • Expose evaluation to optimiser

    data Int = I# Int# plusInt :: Int -> Int -> Int plusInt x y = case x of I# a -> case y of I# b -> I# (a +# b)

    x `plusInt` x = case x of I# a -> case x of I# b -> I# (a +# b) = case x of I# a -> I# (a +# a)

    Library code Inline + optimise

  • So (~#) is the primitive type constructor

    (#) is the primitive language construct

    And (#) is erasable

    data a ~ b = Eq# (a ~# b) () :: (a~b) -> a -> b x c = case c of Eq# d -> x # d refl :: t~t refl = /\t. Eq# (refl# t)

    Library code Inline + optimise

    let c7 = refl Bool in (x c7) && False ...inline refl, = (x # (refl# Bool)) && False

  • User API, and type inference, involves only the lifted (boxed) constraint type (a~b).

    Major benefit (for SLPJ): all forms of evidence are treated uniformly f :: (Eq a, a ~ F b, Num b) => a -> b -> b

    System FC (the intermediate language), has coercion stuff: (~#) and (#). [TLDI07]

    Like types, the FC coercion stuff is fully erasable – guaranteed zero overhead.

    Ordinary, unchanged optimisation removes (almost) all the overhead of boxed coercions.

    Everyone is happy. World peace breaks out.