Dynamic Type Inference

for Gradual Hindley–Milner Typing
!1
Yusuke Miyazaki

Kyoto University

Japan
Taro Sekiyama

National Institute of Informatics

Japan
Atsushi Igarashi

Kyoto University

Japan
POPL 2019 / Cascais, Portugal
Gradual Typing

[Siek & Taha ’06]
Framework to integrate static and dynamic typing in a single
language
• Introduces the dynamic type to specify dynamically typed
parts in a program
• Combines typechecking at compile-time and run-time
• Enables gradual code evolution from a dynamically typed
program to a statically typed program
!2
Gradual Typing: Example
!3
# let g_avg (x: int) (y: *) = (x + y) / 2

val g_avg: int → * → int


# g_avg 3 5

4


# g_avg "hello" 5

Compile-time type error:

parameter “x” expects

int value




# g_avg 3 "hello"

Run-time error:

operator (+) expects int value
Value of any type can be passed

to a parameter with the dynamic type
Whether y has int type or not is
checked at run-timeThe dynamic type
Whether x has int type or not is
checked at compile-time
Gradual Typing: Another Example
!4
# (λf:*. f 3) (λx:int. x)
3: int *


# (λf:*. f 3) (λx:str. x)
Run-time error:

The parameter “x” expects a string value.

But an integer value 3 is passed to the parameter.
λx:int.x is passed to f:* 3 is passed to x:int
Type annotation can affect the result of evaluation
3 is passed to x:str
Value tagged with int
Our Work
Proposes the blame calculus λB
DTI to give a new semantics of the
gradually typed language with type inference and Hindley–Milner
polymorphism
• Introduces dynamic type inference (DTI) to deal with type variables
left undecided by compile-time type inference
• Proves properties of the calculus
• Soundness and completeness of dynamic type inference
• Gradual guarantee of λB
DTI and ITGL
• Implements an interpreter ymyzk/lambda-dti
!5
Outline
• Introduction
• Problem: Undecided Type Variables in ITGL
• Background: Implicitly Typed Gradual Language (ITGL)
• Problem of Type Variables Undecided by Compile-Time Type Inference
• Dynamic Type Inference
• Properties
• Conclusion
!6
Implicitly Typed Gradual Language (ITGL)

[Garcia & Cimini POPL ’15]
• Gradually typed language with let-polymorphism
• Compile-time type inference algorithm that infers static types
for where type annotations are omitted
• Static types: types do not contain the dynamic type
• Principal type property
!7
Compile-Time Type Inference of ITGL
!8
In: (λf. f 3) (λx. x)
Out: (λf:int→int. f 3) (λx:int. x)
Result: 3


In: (λf:*. f 3) (λx. x)
Out: (λf:*. f 3) (λx:α. x)
Add *
Problem of ITGL
!9
In: (λf. f 3) (λx. x)
Out: (λf:int→int. f 3) (λx:int. x)
Result: 3


In: (λf:*. f 3) (λx. x)
Out: (λf:*. f 3) (λx:α. x)
Result1: 3: int * (if α is replaced with int before evaluation)
Result2: Run-time error (otherwise)
Left undecided by type inference,

but cannot be ignored at run-time
Add *
bool int str
int→int int→bool

int→str bool→int …
Generally, it is difficult to find a static type for α at compile-time !
Candidates for α
Our Solution
!10
Find static types for undecided type variables during evaluation
(λf:*. f 3) (λx:α. x)
Run-time system will find out α to be int
Another Idea: Replacing with the Dynamic Type
!11
Replacing undecided type variables with the dynamic type may
sounds like a good idea, but we find some problems:
• Goes against with the original idea of ITGL that a type variable
is a placeholder for a static type
• Makes it difficult to detect a type error earlier
• Because it can make evaluation successful even when there
is no static substitutions that makes evaluation successful
We decided not to use this approach "
Outline
• Introduction
• Problem: Undecided Type Variables in ITGL
• Dynamic Type Inference
• Background: Cast for Run-Time Typecheck
• Dynamic Type Inference
• Properties
• Conclusion
!12
Cast for Run-Time Typecheck

[Wadler & Fiedler ESOP ’09]
A term for performing run-time typecheck in blame calculi
e: U U’
• Cast failure is expressed as a special term blame
• The paper deals with blame labels formally
• Casts are inserted where run-time checks are required during
translation from a gradually typed language to a blame
calculus
!13
Cast of a term e from type U to U’
Examples: Run-Time Typecheck with Casts
!14
(λx:*. x + 2) 3
⇝ (λx:*. (x: * int) + 2) (3: int *)
⟼ (3: int * int) + 2
⟼ 3 + 2 ⟼ 5

(λx:*. x + 2) "hello"
⇝ (λx:*. (x: * int) + 2) ("hello": str *)
⟼ ("hello": str * int) + 2
⟼ blame + 2 ⟼ blame
Run-time typecheck:

cast from int to int succeeds
Run-time typecheck:

cast from str to int fails
Value tagged with int
Cast inserting
translation
Another Example: Run-Time Typecheck with Casts
!15
(λf:*. f 3) (λx:int. x)
⇝ (λf:*. (f: * *→*) (3: int *))

                 ((λx:int. x): int→int *)


⟼+((λx:int. x) (3: int * int)): int *


⟼ ((λx:int. x) 3): int *
⟼+ 3: int *
Run-time typecheck:
cast from int to int succeeds
Dynamic Type Inference
!16
(λf:*. f 3) (λx:α. x)
⇝ (λf:*. (f: * *→*) (3: int *))

                   ((λx:α. x): α→α *)
⟼+((λx:α. x) (3: int * α)): α *






⟼ ((λx:int. x) 3): int *
⟼+ 3: int *
α is instantiated with int
DTI: Instantiates α with the type attached to

the value (int) and proceeds the evaluation
Generated substitution:

[α↦int]
Resulted in a value thanks to DTI
Left undecided by compile-time inference
Simply typed blame calculus + type variables

(+ let-polymorphism discussed in the paper)

U ::= * (Dynamic Type)

| α (Type Variable)

| int (Base Type)

| U→U (Function Type)
Formalizing DTI: λB
DTI
!17
e ::= x (Variable)

| c (Constant)

| λx:U.e (Abstraction)

| e1 e2 (Application)
| e: U1 U2 (Cast)
| blame (Blame)
Formalizing DTI: Reduction Relation
Two reduction rules for dynamic type inference
!18
v: *→* * α  ⟶  v: *→* * α1→α2
[α↦α1→α2]
Type substitution obtained by DTI:

Instantiate α with int then proceed reduction
v: int * α  ⟶ v
[α↦int]
e ⟶ e’S
Generate fresh type
variables α1 and α2
Formalizing DTI: Evaluation Relation
The evaluation rule makes sure that a type variable is
instantiated at most once










Example:
!19
e ⟶ e’

E[e] ⟼ S(E[e’])S
Apply the type substitution S to the entire term
S
Evaluation context
Reduction of a subterm
3: int * α ⟶ 3

(λx:α. x) (3: int * α) ⟼ (λx:int. x) 3
[α↦int]
[α↦int]
e ⟼ e’S
Outline
• Introduction
• Problem: Undecided Type Variables in ITGL
• Dynamic Type Inference
• Properties
• Conclusion
!20
Properties
Prove the following properties of the calculus
• Type safety of λB
DTI
• Soundness and completeness of DTI
• Translation from ITGL to λB
DTI is type-preserving
• Gradual guarantee of λB
DTI and ITGL
!21
Soundness and Completeness of DTI
DTI always returns an “appropriate” type substitution if it exists



• Soundness of DTI:
• A type substitution yielded by DTI is appropriate
• Completeness of DTI:
• If an appropriate type substitution exists, DTI returns one
!22
By applying it before evaluation,

the program terminates at a value
Soundness of Dynamic Type Inference
1. If a program e results in a value or blame r with DTI, then
applying the substitution S obtained by DTI beforehand does
not change the result
2. If a program e evaluates to blame with DTI, applying any
substitution results in blame
!23
Theorem (Soundness): Suppose ⊢ e: U
1. If e ⟼* r then, for any S’ s.t. ftv(S’(S(e))) = ∅, S’(S(e)) ⟼* S’(r)

for some S’’
2. If e ⟼* blame then, for any S’, S’(e) ⟼* blame for some S’’
S
S
S’’
S’’
Completeness of Dynamic Type Inference
1. If there is a type substitution S that makes a program result in
a value v, then evaluation with DTI also results in a related
value v’
2. If there is a type substitution that makes a program diverge,
evaluation with DTI also diverges
!24
Theorem (Completeness): Suppose ⊢ e: U
1. If S(e) ⟼* v, then e ⟼* v’ and S’’(v’) = v for some v’, S’, S’’
2. If S(e) diverges, then e diverges
∅ S’
In the Paper and Artifact…
• Full definition of the calculus λB
DTI
• Syntax, type system, operational semantics, translation from ITGL
• Extension to let-polymorphism which is non-parametric [Ahmed et al. POPL ’11]
• let x = v in e behaves the same as e[x:=v]
• Properties of the calculus
• Type safety
• Soundness and completeness of DTI
• Gradual guarantee of λB
DTI and ITGL with non-trivial precision relation
• Implementation of the calculus
!25
Related Work
• Replacing undecided type variables with the dynamic type

[Henglein & Rehof FPCA ’95 / Xie et al. ESOP ’18]
• Their work:
• Provides more permissive semantics
• Our work:
• Enables earlier bug detection
• Consistent with the idea that a type variable in ITGL is a
placeholder for a static type
!26
Conclusion
Blame calculus with dynamic type inference and let-polymorphism
• Dynamic type inference
• Instantiates undecided type variables along with evaluation
• Let-Polymorphism
• Properties
• Soundness and completeness of DTI
• Gradual guarantee of λB
DTI and ITGL
!27

Dynamic Type Inference for Gradual Hindley–Milner Typing

  • 1.
    Dynamic Type Inference
 forGradual Hindley–Milner Typing !1 Yusuke Miyazaki
 Kyoto University
 Japan Taro Sekiyama
 National Institute of Informatics
 Japan Atsushi Igarashi
 Kyoto University
 Japan POPL 2019 / Cascais, Portugal
  • 2.
    Gradual Typing
 [Siek &Taha ’06] Framework to integrate static and dynamic typing in a single language • Introduces the dynamic type to specify dynamically typed parts in a program • Combines typechecking at compile-time and run-time • Enables gradual code evolution from a dynamically typed program to a statically typed program !2
  • 3.
    Gradual Typing: Example !3 #let g_avg (x: int) (y: *) = (x + y) / 2
 val g_avg: int → * → int 
 # g_avg 3 5
 4 
 # g_avg "hello" 5
 Compile-time type error:
 parameter “x” expects
 int value 
 
 # g_avg 3 "hello"
 Run-time error:
 operator (+) expects int value Value of any type can be passed
 to a parameter with the dynamic type Whether y has int type or not is checked at run-timeThe dynamic type Whether x has int type or not is checked at compile-time
  • 4.
    Gradual Typing: AnotherExample !4 # (λf:*. f 3) (λx:int. x) 3: int * 
 # (λf:*. f 3) (λx:str. x) Run-time error:
 The parameter “x” expects a string value.
 But an integer value 3 is passed to the parameter. λx:int.x is passed to f:* 3 is passed to x:int Type annotation can affect the result of evaluation 3 is passed to x:str Value tagged with int
  • 5.
    Our Work Proposes theblame calculus λB DTI to give a new semantics of the gradually typed language with type inference and Hindley–Milner polymorphism • Introduces dynamic type inference (DTI) to deal with type variables left undecided by compile-time type inference • Proves properties of the calculus • Soundness and completeness of dynamic type inference • Gradual guarantee of λB DTI and ITGL • Implements an interpreter ymyzk/lambda-dti !5
  • 6.
    Outline • Introduction • Problem:Undecided Type Variables in ITGL • Background: Implicitly Typed Gradual Language (ITGL) • Problem of Type Variables Undecided by Compile-Time Type Inference • Dynamic Type Inference • Properties • Conclusion !6
  • 7.
    Implicitly Typed GradualLanguage (ITGL)
 [Garcia & Cimini POPL ’15] • Gradually typed language with let-polymorphism • Compile-time type inference algorithm that infers static types for where type annotations are omitted • Static types: types do not contain the dynamic type • Principal type property !7
  • 8.
    Compile-Time Type Inferenceof ITGL !8 In: (λf. f 3) (λx. x) Out: (λf:int→int. f 3) (λx:int. x) Result: 3 
 In: (λf:*. f 3) (λx. x) Out: (λf:*. f 3) (λx:α. x) Add *
  • 9.
    Problem of ITGL !9 In:(λf. f 3) (λx. x) Out: (λf:int→int. f 3) (λx:int. x) Result: 3 
 In: (λf:*. f 3) (λx. x) Out: (λf:*. f 3) (λx:α. x) Result1: 3: int * (if α is replaced with int before evaluation) Result2: Run-time error (otherwise) Left undecided by type inference,
 but cannot be ignored at run-time Add * bool int str int→int int→bool
 int→str bool→int … Generally, it is difficult to find a static type for α at compile-time ! Candidates for α
  • 10.
    Our Solution !10 Find statictypes for undecided type variables during evaluation (λf:*. f 3) (λx:α. x) Run-time system will find out α to be int
  • 11.
    Another Idea: Replacingwith the Dynamic Type !11 Replacing undecided type variables with the dynamic type may sounds like a good idea, but we find some problems: • Goes against with the original idea of ITGL that a type variable is a placeholder for a static type • Makes it difficult to detect a type error earlier • Because it can make evaluation successful even when there is no static substitutions that makes evaluation successful We decided not to use this approach "
  • 12.
    Outline • Introduction • Problem:Undecided Type Variables in ITGL • Dynamic Type Inference • Background: Cast for Run-Time Typecheck • Dynamic Type Inference • Properties • Conclusion !12
  • 13.
    Cast for Run-TimeTypecheck
 [Wadler & Fiedler ESOP ’09] A term for performing run-time typecheck in blame calculi e: U U’ • Cast failure is expressed as a special term blame • The paper deals with blame labels formally • Casts are inserted where run-time checks are required during translation from a gradually typed language to a blame calculus !13 Cast of a term e from type U to U’
  • 14.
    Examples: Run-Time Typecheckwith Casts !14 (λx:*. x + 2) 3 ⇝ (λx:*. (x: * int) + 2) (3: int *) ⟼ (3: int * int) + 2 ⟼ 3 + 2 ⟼ 5
 (λx:*. x + 2) "hello" ⇝ (λx:*. (x: * int) + 2) ("hello": str *) ⟼ ("hello": str * int) + 2 ⟼ blame + 2 ⟼ blame Run-time typecheck:
 cast from int to int succeeds Run-time typecheck:
 cast from str to int fails Value tagged with int Cast inserting translation
  • 15.
    Another Example: Run-TimeTypecheck with Casts !15 (λf:*. f 3) (λx:int. x) ⇝ (λf:*. (f: * *→*) (3: int *))
                  ((λx:int. x): int→int *) 
 ⟼+((λx:int. x) (3: int * int)): int * 
 ⟼ ((λx:int. x) 3): int * ⟼+ 3: int * Run-time typecheck: cast from int to int succeeds
  • 16.
    Dynamic Type Inference !16 (λf:*.f 3) (λx:α. x) ⇝ (λf:*. (f: * *→*) (3: int *))
                    ((λx:α. x): α→α *) ⟼+((λx:α. x) (3: int * α)): α * 
 
 
 ⟼ ((λx:int. x) 3): int * ⟼+ 3: int * α is instantiated with int DTI: Instantiates α with the type attached to
 the value (int) and proceeds the evaluation Generated substitution:
 [α↦int] Resulted in a value thanks to DTI Left undecided by compile-time inference
  • 17.
    Simply typed blamecalculus + type variables
 (+ let-polymorphism discussed in the paper)
 U ::= * (Dynamic Type)
 | α (Type Variable)
 | int (Base Type)
 | U→U (Function Type) Formalizing DTI: λB DTI !17 e ::= x (Variable)
 | c (Constant)
 | λx:U.e (Abstraction)
 | e1 e2 (Application) | e: U1 U2 (Cast) | blame (Blame)
  • 18.
    Formalizing DTI: ReductionRelation Two reduction rules for dynamic type inference !18 v: *→* * α  ⟶  v: *→* * α1→α2 [α↦α1→α2] Type substitution obtained by DTI:
 Instantiate α with int then proceed reduction v: int * α  ⟶ v [α↦int] e ⟶ e’S Generate fresh type variables α1 and α2
  • 19.
    Formalizing DTI: EvaluationRelation The evaluation rule makes sure that a type variable is instantiated at most once 
 
 
 
 
 Example: !19 e ⟶ e’
 E[e] ⟼ S(E[e’])S Apply the type substitution S to the entire term S Evaluation context Reduction of a subterm 3: int * α ⟶ 3
 (λx:α. x) (3: int * α) ⟼ (λx:int. x) 3 [α↦int] [α↦int] e ⟼ e’S
  • 20.
    Outline • Introduction • Problem:Undecided Type Variables in ITGL • Dynamic Type Inference • Properties • Conclusion !20
  • 21.
    Properties Prove the followingproperties of the calculus • Type safety of λB DTI • Soundness and completeness of DTI • Translation from ITGL to λB DTI is type-preserving • Gradual guarantee of λB DTI and ITGL !21
  • 22.
    Soundness and Completenessof DTI DTI always returns an “appropriate” type substitution if it exists
 
 • Soundness of DTI: • A type substitution yielded by DTI is appropriate • Completeness of DTI: • If an appropriate type substitution exists, DTI returns one !22 By applying it before evaluation,
 the program terminates at a value
  • 23.
    Soundness of DynamicType Inference 1. If a program e results in a value or blame r with DTI, then applying the substitution S obtained by DTI beforehand does not change the result 2. If a program e evaluates to blame with DTI, applying any substitution results in blame !23 Theorem (Soundness): Suppose ⊢ e: U 1. If e ⟼* r then, for any S’ s.t. ftv(S’(S(e))) = ∅, S’(S(e)) ⟼* S’(r)
 for some S’’ 2. If e ⟼* blame then, for any S’, S’(e) ⟼* blame for some S’’ S S S’’ S’’
  • 24.
    Completeness of DynamicType Inference 1. If there is a type substitution S that makes a program result in a value v, then evaluation with DTI also results in a related value v’ 2. If there is a type substitution that makes a program diverge, evaluation with DTI also diverges !24 Theorem (Completeness): Suppose ⊢ e: U 1. If S(e) ⟼* v, then e ⟼* v’ and S’’(v’) = v for some v’, S’, S’’ 2. If S(e) diverges, then e diverges ∅ S’
  • 25.
    In the Paperand Artifact… • Full definition of the calculus λB DTI • Syntax, type system, operational semantics, translation from ITGL • Extension to let-polymorphism which is non-parametric [Ahmed et al. POPL ’11] • let x = v in e behaves the same as e[x:=v] • Properties of the calculus • Type safety • Soundness and completeness of DTI • Gradual guarantee of λB DTI and ITGL with non-trivial precision relation • Implementation of the calculus !25
  • 26.
    Related Work • Replacingundecided type variables with the dynamic type
 [Henglein & Rehof FPCA ’95 / Xie et al. ESOP ’18] • Their work: • Provides more permissive semantics • Our work: • Enables earlier bug detection • Consistent with the idea that a type variable in ITGL is a placeholder for a static type !26
  • 27.
    Conclusion Blame calculus withdynamic type inference and let-polymorphism • Dynamic type inference • Instantiates undecided type variables along with evaluation • Let-Polymorphism • Properties • Soundness and completeness of DTI • Gradual guarantee of λB DTI and ITGL !27