A Logic for Duck Typing Ravi Chugh Pat Rondon Ranjit Jhala UCSD 2 What are Dynamic Languages let onto callbacks f obj if f null then new List obj callbacks ID: 350206
Download Presentation The PPT/PDF document "Nested Refinements:" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Nested Refinements:A Logic for Duck Typing
Ravi Chugh, Pat Rondon, Ranjit Jhala (UCSD)
::Slide2
2What are “Dynamic Languages”?
let onto callbacks f obj
=
if
f
=
null then new List(obj, callbacks) else let cb = if tagof f = “Str” then obj[f] else f in new List(fun () -> cb obj, callbacks)
tag tests
affect control flow
dictionary objects
indexed by arbitrary string keys
first-class functions
can appear inside objectsSlide3
3
Problem: Lack of static types
… makes rapid prototyping / multi-language applications easy
… makes reliability / performance / maintenance hard
This Talk:
System D
… a type system for these featurestag testsaffect control flowdictionary objectsindexed by arbitrary string keysfirst-class functionscan appear inside objectsSlide4
…
occurrence types
∨, ∧
Our Approach:
Quantifier-free formulas
tag tests
affect control flowdictionary objectsindexed by arbitrary string keysfirst-class functionscan appear inside objectsExpressivenessUsabilityF≤
Coq
refinement types
nested refinements
syntactic approaches
dependent approaches
1. increase expressiveness
2. retain level of automationSlide5
5
Functions inside dictionaries
Challenge:
{
|tag() = “Int”tag() = “Bool”}x ::{
|tag
(
)
=
“Dict”
tag(
sel
(,“n”
))
=
“Int”
tag
(
sel
(
,
m
))
=
“
Int”
}
d
::
tag tests
affect control flow
dictionary objects
indexed by arbitrary string keys
first-class functions
can appear inside objectsSlide6
6Key Idea: Nested Refinements
{
|
tag
() = “Dict” sel(,f) :: }d ::
{
|
tag
(
)=“Int
”
}
{
|tag(
)=
“Int” }
uninterpreted
predicate
“
x
::
U
” says
“
x
has-type
U
”
syntactic arrow type…
1 + d[f](0)Slide7
7Key Idea: Nested Refinements
{
|
tag
() = “Dict” sel(,f) :: }d ::
{
|
tag
(
)=“Int
”
}
{
|tag(
)=
“Int” }
uninterpreted
predicate
“
x
::
U
” says
“
x
has-type
U
”
1 + d[f](0)
syntactic arrow type…
… but uninterpreted constant in the logicSlide8
All values described by refinement formulas
T ::=
{
|
p}“Has-type” predicate for arrows in formulasp ::= … | x :: y:T1T2Can express idioms of dynamic languagesAutomatic type checkingDecidable refinement logicSubtyping = SMT Validity + Syntactic Subtyping8Key Idea: Nested RefinementsSlide9
OutlineIntroExamples
SubtypingType SoundnessConclusion
9Slide10
10
let negate x = if
tagof x =
“
Int
” then 0 – x else not xy:Top{| = tag(y)}tagof ::
x:
{
|
tag
(
)
= “Int”
tag()
=
“
Bool”}
{
|
tag
(
)
=
tag
(
x
)
}
x:IntOrBool
{
|
tag
(
)
=
tag
(
x
)
}
y:
{
|
true
}Slide11
IntOrBool
x
::
Γ
11
{
|Int()}x ::{
|
Int(
)
}
0 - x
::
x:IntOrBool
{
|tag(
)
=
tag
(
x
)
}
let
negate x
=
if
tagof x
=
“
Int
”
then
0 – x
else
not x
tag
(
x
)
=
“
Int
”
type environment
✓
✓
SMT Solver
✓Slide12
IntOrBool
x
::
Γ
12
{
|Bool()}x ::{
|
Bool(
)
}
not x
::
x:IntOrBool
{
|tag
(
)
=
tag
(
x
)
}
let
negate x
=
if
tagof x
=
“
Int
”
then
0 – x
else
not x
not
(
tag
(
x
)
=
“
Int
”
)
type environment
SMT Solver
✓
✓
✓Slide13
13
{
|
::
}x:IntOrBool{|tag() = tag(x) }x:IntOrBool
{
|
tag
(
)
= tag(x
)
}Nesting structure hidden with syntactic sugarSlide14
Dictionary Operations14
d:Dict
k
:Str
{| = true has(d,k)}mem ::d:Dictk:{
|
has
(d,
)
}
{
|
= sel(
d,
k
)}
get
::
d:Dict
k
:Str
x
:Top
{
|
=
upd
(
d
,
k
,
x
)
}
set
::
Types in terms of McCarthy operatorsSlide15
15
let getCount d c = if
mem d c then
toInt
(
d
[c]) else 0d:Dictc:StrInt{| = true has(d,c)
}
safe dictionary key lookup
get d cSlide16
16
let incCount d c = let
i =
getCount
d c
in {d with c = i + 1}d:Dictc:StrIntd:Dictc:Str{|EqMod(
,
d,c
)
Int(
[c
])
}
tag
(
sel
(
,c
))
=
“
Int”
let
getCount d c
=
if
mem d c
then
toInt
(
d
[
c
])
else
0
set d c (i+1)Slide17
T ::=
{
|
p
}p ::= … | x :: UU ::= y:T1T2
| A |
List
T
| Null
17Adding Type Constructors
“type terms”Slide18
18
let apply f x = f
x
∀A,B.
{
|:: {|:: A }{|:: B } }
{
|
::
A }
{
|
:: B
}
∀A,B.
(
A
B
)
A
BSlide19
19
let dispatch d f = d[
f](
d
)
∀A,B.
d:{| :: A } f:{|d[] :: AB }
{
|
::
B
}
d
::
A but additional constraints on A≈ ∀A
<:
{f:
A B}. d ::
A
a form of
“bounded quantification”Slide20
20
let map f
xs
=
if
xs
= null then null else new List(f xs[“hd”], map f xs[“tl”])∀A,B. {|:: AB
}
{
|
::
List[A]
}
{
|
:: List
[
B
]
}
encode recursive data as dictionaries
∀A,B.
(
A
B
)
List
[
A
]
List
[
B
]Slide21
21
let filter f
xs =
if
xs
= null then null else if not (f xs[“hd”]) then filter f xs[“tl”] else new List(xs[“hd”], filter f xs[“tl”])
∀A,B. (
x:A
{
|
n = true
x:: B
}
List[A]
List
[
B
]
usual definition,
but an interesting typeSlide22
OutlineIntroExamples
SubtypingType SoundnessConclusion
22Slide23
23
SMT
Γ
=42 tag() = “Int” Γ {|= 42} < Int
_
|
(
Int,
Int
Int)
Int
applyInt ::
applyInt
(
42,
negate
)
x:IntOrBool
{
|
tag
(
)
=
tag
(
x
)
}
negate ::
Γ
✓
type environmentSlide24
SMT
…
negate
:: x:IorB{|tag() = tag(x)}… =negate ::
Int
Int
24
Γ
{
|
=
negate
} < {
|
::
Int
Int
}
_
|
(
Int,
Int
Int
)
Int
applyInt
::
applyInt
(
42,
negate
)
x:IntOrBool
{
|
tag
(
)
=
tag
(
x
)
}
negate ::
Γ
type environmentSlide25
25
Γ
{
|= negate } < {|:: IntInt }
_
|
(
Int,
Int
Int)
Int
applyInt
::
applyInt (
42,
negate
)
x:IntOrBool
{
|
tag
(
)
=
tag
(
x
)
}
negate ::
Γ
✗
distinct uninterpreted constants!
type environment
SMT
…
negate
::
x:IorB
{
|
tag
(
)
=
tag
(
x
)
}
…
=
negate
::
Int
IntSlide26
26
IorB {
|
tag() = tag(x)} <: Int IntInt <: IorB{|tag() = tag(x)
} <: Int
tag
(
)
= “Int”
tag
() =
“
Int
”
tag
(
)
=
“
Bool”
tag
(
)
=
“
Int
”
tag
(
)
=
tag
(
x
)
tag
(
)
=
“
Int
”
Invalid, since these are uninterpreted constants
W
ant conventional syntactic subtyping
✓
✓
::
x:IorB
{
|
tag
(
)
=
tag
(
x
)
}
::
Int
Int
✗Slide27
Subtyping with Nesting27
base predicate: p
q
ij
1) Convert
q
to CNF clauses (q11 … ) … (qn1 … )2) For each clause, discharge some literal qij as follows:To prove p q :
anything except
x
::
U
e.g.
tag
(
) = tag(
x)
e.g.
tag(sel
(
d
,
k
))
=
“
Int
”Slide28
Subtyping with Nesting28
Implication
SMT Solver
Subtyping
Arrow Rule
base predicate:
p qij“has-type” predicate: p x :: UImplicationSMT SolverSubtyping
p
x ::
U’
U’
<: U
p
qij
1) Convert
q
to CNF clauses (q11
…
)
…
(q
n1
…
)
2) For each clause, discharge some literal
q
ij
as follows:
To prove
p
q
:Slide29
Uninterpreted
Reasoning
…
negate:: x:IorB{|tag() = tag(x)}…
=negate
::
x:IorB
{
|
tag()
=
tag(x)
}
29
applyInt
(
42,
negate
)
Γ
{
|
=
negate
}
<
{
|
::
Int
Int
}
_
|
+
Syntactic
Reasoning
Γ
x:IorB
{
|
tag
(
)
=
tag
(
x
)
}
<:
Int
Int
_
|Slide30
OutlineIntroExamples
SubtypingType SoundnessConclusion
30Slide31
31
0 :: {
|
x.x+1:: IntInt }_|x.x+1 :: {|:: IntInt }
_
|
f:
{
|
::
Int
Int }
0
:: {
|
f
::
Int
Int
}
_
|
Substitution
Lemma
_
|
v ::
T
x
and
If
x:T
x
,
Γ e
[
::
T
_
|
Γ
[
v
/
x
]
e
[
v
/
x
]
::
T
[
v
/
x
]
_
|
then
independent of
0
, and just echoes the binding from the environmentSlide32
32
0
::
{
|x.x+1:: IntInt }_|SMT=0
x.x+1
:: Int
Int
✗
{
|
=
0 } < {
|
x.x+1
::
Int
Int
}
0
::
{
|
=
0
}
Substitution
Lemma
_
|
v ::
T
x
and
If
x:T
x
,
Γ e
[
::
T
_
|
Γ
[
v
/
x
]
e
[
v
/
x
]
::
T
[
v
/
x
]
_
|
then
1
st
attemptSlide33
33
0
::
{
|x.x+1:: IntInt }_|{|=
0 }
< {
|
x.x+1
::
Int
Int
}0
::
{
|
=
0
}
Substitution
Lemma
_
|
v ::
T
x
and
If
x:T
x
,
Γ e
[
::
T
_
|
Γ
[
v
/
x
]
e
[
v
/
x
]
::
T
[
v
/
x
]
_
|
then
✗
SMT
=
0
::
U’
Arrow
U’ <:
Int
Int
+
2
nd
attempt
✗Slide34
34
x.x+1
::
IntInt|=Ix.x+1:: {|:: Int
Int
}
|
_
SMT
Γ
p
q
Γ
{
|
=
p
}
<
{
|
=
q
}
_
|
[S-Valid-Uninterpreted]
|
=
I
Γ
p
q
Γ
{
|
=
p
}
<
{
|
=
q
}
_
|
[S-Valid-Interpreted]
iff
Rule not closed under substitution
Interpret formulas by “hooking back” into type system
Stratification to create ordering for induction
n
n-1
n
nSlide35
Type Soundness35
StratifiedSubstitution
Lemma
_
|
v ::
TxandIfx:Tx, Γ e[:: T_|Γ[v/x] e
[v/
x]
:: T
[
v/
x]
_
|
then
n+1
n
n
Stratified
Preservation
e
v
and
If
e
[
::
T
_
|
0
_
|
then
v
[
::
T
m
for some m
“Level 0”
for type checking source programs,
using
only [S-Valid-Uninterpreted]
artifact
of the metatheorySlide36
RecapDynamic languages make heavy use of:run-time tag tests, dictionary objects, lambdasNested refinementsgeneralizes refinement type architectureenables combination of dictionaries and lambdas
Decidable refinement logicall proof obligations discharged algorithmicallynovel subtyping decomposition to retain precisionSyntactic type soundness36Slide37
Future WorkImperative UpdatesInheritance (prototypes in JS, classes in Python)ApplicationsMore local type inference / syntactic sugarDictionaries in statically-typed languages
37Slide38
38Thanks!
D
ravichugh.com/nested
::Slide39
39Extra SlidesSlide40
Constants40
tagof::x:Top
{
| = tag(x)}mem::d:Dictk:Str {|Bool()
=
True
has
(d
,k)
}get::d:Dict
k:{
|Str(
)
has
(
d
,
)
}
{
|
=
sel
(
d
,
k
)
}
set
::
d:Dict
k
:Str
x
:Top
{
|
=
upd
(
d
,
k
,
x
)
}
rem
::
d:Dict
k
:Str
{
|
=
upd
(
d
,
k
,
bot
)
}Slide41
TypesFormulasLogical ValuesMacros
41
{
|
tag()=“Int” }Int x:T1T2 {
|
::
}
x:T
1
T
2
tag
(
x) = “Str”
Str(x)
sel
(
n
,
“k”
)
x.k
sel
(
n
,
k
)
x[k]
sel
(
d
,
k
)
!=
bot
has(d,k)
∀k’. k’
!=
k
sel
(
d
,
k
)
!=
sel
(
d’
,
k
)
EqMod(d,d’,k)
Slide42
Onto42
let onto callbacks f obj =
if
f
=
null
then new List(obj,callbacks) else let cb = if tagof f = “Str” then obj[f] else f in new List(fun () -> cb obj, callbacks)
∀A.
callbacks:List
[Top
Top]
f:{
|=
null
Str(
)
::
A
Top
}
obj
:
{
|
::
A
(
f
=
null
::
A
Int
)
(
Str
(
f
)
[
f
]
::
A
Int
)
}
List
[
Top
Top
]
onto ::
functional version of Dojo functionSlide43
Onto (2)43
let onto (callbacks,f,obj
)
=
if
f
= null then new List(obj,callbacks) else let cb = if tagof f = “Str” then obj[f] else f in new List(fun () ->
cb obj, callbacks)
callbacks:
List
[
Top
Top
]
*
f:{
g|g
=
null Str
(
g
)
g
::
{
x
|
x
=
obj
}
Top
}
*
obj
:
{
o
|
(
f
=
null
o
::
{
x
|
x
=
o
}
Int
)
(
Str
(
f
)
o
[
f
]
::
{
x
|
x
=
o
}
Int
)
}
List
[
Top
Top
]
onto ::
functional version of Dojo functionSlide44
44
Traditional vs. Nested RefinementsSlide45
45Reuse refinement type architectureFind a decidable refinement logic forTag-tests
DictionariesLambdasDefine nested refinement type architectureApproach: Refinement Types
✓
✗
✓
✓
*Slide46
46Nested Refinements
T ::= {|
p
}
U ::= x:T
1
T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x :: URefinement formulas over a decidable logicuninterpreted functions, McCarthy arrays, linear arithmeticOnly base values refined by formulas
All values
T ::= {
|
p
}
| x:T1
T2p
::= p
q | … |
x
=
y
|
x
<
y
| …
|
tag(x
) = “
Int
” | …
|
sel(x,y
) =
z
| …
traditional refinementsSlide47
47Nested Refinements
T ::= {|
p
}
U ::= x:T
1
T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::URefinement formulas over a decidable logic
uninterpreted
functions, McCarthy arrays, linear arithmetic
Only base values refined by formulas
“has-type” allows “type terms” in formulas
All values
T ::= {
|
p
} | x:T1
T
2
p ::= p
q
| …
|
x
=
y
|
x
<
y
| …
|
tag(x
) = “
Int
” | …
|
sel(x,y
) =
z
| …
traditional refinementsSlide48
48Nested Refinements
T ::= {|
p
}
U ::= x:T
1
T2p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::URefinement formulas over a decidable logicuninterpreted functions, McCarthy arrays, linear arithmeticOnly base values refined by formulas“has-type” allows “type terms” in formulasAll valuesSlide49
Subtyping (Traditional Refinements)49
T ::= {|
p
}
| x:T
1T2traditional refinementsImplicationSMT SolverSubtypingtag()=“Int” trueInt <: TopSlide50
Subtyping (Traditional Refinements)50
T ::= {|
p
}
| x:T
1T2traditional refinementsImplicationSMT SolverSubtypingTop Int <: Int IntInt <: TopInt <: Int
tag
(
)=“Int
”
true
tag
(
)=
“Int”
tag
(
)=“Int”Slide51
Subtyping (Traditional Refinements)51
T ::= {|
p
}
| x:T
1T2traditional refinementsImplicationSMT SolverSubtypingTop Int <: Int IntInt <: TopInt <: Int
tag
(
)=“Int
”
true
tag
(
)=
“Int”
tag
(
)=“Int”
Arrow RuleSlide52
Subtyping (Traditional Refinements)52
T ::= {|
p
}
| x:T
1T2traditional refinementsImplicationSMT SolverSubtypingArrow RuleDecidable if:Only values in formulas
Underlying theories decidable