Skip to content

Commit

Permalink
start adding tests to juliatypes.jl
Browse files Browse the repository at this point in the history
the subtype algorithm is currently tweaked to reproduce the behavior of F_<:.
running non_terminating() demonstrates execution on the non-terminating
instance from the paper.
  • Loading branch information
JeffBezanson committed Nov 17, 2014
1 parent cdc4225 commit 23b6a1f
Showing 1 changed file with 112 additions and 17 deletions.
129 changes: 112 additions & 17 deletions examples/juliatypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,42 @@ end

type Var
name::Symbol
lb::Ty
ub::Ty
lb
ub
Var(n, lb=BottomT, ub=AnyT) = new(n, lb, ub)
end

function show(io::IO, v::Var)
function show_var_bounds(io::IO, v::Var)
if v.lb !== BottomT
show(io, v.lb)
print(io, "<:")
end
print(io, v.name, "<:")
show(io, v.ub)
print(io, v.name)
if v.ub !== AnyT
print(io, "<:")
show(io, v.ub)
end
end

show(io::IO, v::Var) = print(io, v.name)

type ForAllT <: Ty
var::Var
T::Ty
T
ForAllT(v::Var, t) = new(v, t)
ForAllT(v::Var, t::Type) = new(v, convert(Ty, t))
end

function show(io::IO, x::ForAllT)
print(io, "@UnionAll ")
show(io, x.var)
print(io, " ")
if x.T === x.var && x.var.ub !== AnyT
print(io, "¬", x.var.ub)
return
end
print(io, "(∀ ")
show_var_bounds(io, x.var)
print(io, ". ")
show(io, x.T)
print(io, ")")
end

AnyT = TagT(TypeName(:Any), ())
Expand All @@ -62,7 +74,7 @@ inst(typename::TypeName, params...) = TagT(typename, params)

inst(t::TagT) = t

inst(t::ForAllT, param) = subst(t.T, Dict(t.var => param))
inst(t::ForAllT, param) = subst(t.T, Dict{Any,Any}(t.var => param))
inst(t::ForAllT, param, rest...) = inst(inst(t,param), rest...)

super(t::TagT) = inst(t.name.super, t.params...)
Expand All @@ -85,6 +97,8 @@ tupletype(xs...) = inst(TupleName, xs...)

isequal_type(x::Ty, y::Ty) = issub(x, y, true)

issub(x, y, env, inv) = (x === y)

function issub(x::Ty, y::Ty, inv::Bool = false)
env = Dict()
ans = issub(x, y, env, inv)
Expand Down Expand Up @@ -200,7 +214,22 @@ function add_constraint!(env, var, c::SupConstraint)
end
end

ctr = 1
rename(x) = x
function rename(x::ForAllT)
global ctr
v = Var(symbol(string("a",ctr)), x.var.lb, rename(x.var.ub))
ctr += 1
ForAllT(v, rename(inst(x, v)))
end

function issub_var(a::Var, b, env, invariant)
if b === AnyT
return true
end
if haskey(env, a) && !invariant
return issub(rename(env[a][1].rhs), b, env, invariant)
end
add_constraint!(env, a, invariant ? EqConstraint(b) : SubConstraint(b))
return true
end
Expand All @@ -220,17 +249,30 @@ function issub(a::Ty, b::Var, env, invariant)
end

function issub(a::ForAllT, b::ForAllT, env, invariant)
println(a, " <: ", b); sleep(.5)

# 1. handle bounds
# this could be the crucial bit: we make the bounds covariant
if !(issub(a.var.ub, b.var.ub, env, false) &&
issub(b.var.lb, a.var.lb, env, false))
if !(issub(b.var.ub, a.var.ub, copy(env), false) &&
issub(a.var.lb, b.var.lb, copy(env), false))
return false
end

# 2. handle expression
fresh = Var(symbol(string("_",a.var.name,"_",b.var.name)), a.var.lb, a.var.ub)
add_constraint!(env, fresh, SupConstraint(a.var.lb))
add_constraint!(env, fresh, SubConstraint(a.var.ub))
inner = issub(inst(a, fresh), inst(b, fresh), env, invariant)
# for contravariance, fresh<:b.var.ub, for covariance fresh<:a.var.ub
lb = b.var.lb
ub = b.var.ub
#var = Var(symbol(string("a",ctr)), lb, ub)
#global ctr += 1
var = b.var

env = copy(env)
#add_constraint!(env, var, SupConstraint(lb))
add_constraint!(env, var, SubConstraint(ub))

a = inst(a, var)
b = b.T

inner = issub(a, b, env, invariant)
return inner
end

Expand Down Expand Up @@ -302,6 +344,8 @@ const tndict = ObjectIdDict()

xlate(t) = xlate(t, ObjectIdDict())

xlate(t, env) = t

function xlate(t::UnionType, env)
if t === Union()
return BottomT
Expand Down Expand Up @@ -365,3 +409,54 @@ ArrayT =
let ArrayName = TypeName(:Array, @UnionAll T @UnionAll N inst(AbstractArrayT, T, N))
@UnionAll T @UnionAll N inst(ArrayName, T, N)
end

function non_terminating()
# undecidable F_<: instance
¬T = @UnionAll α<:T α

θ = @UnionAll α ¬(@UnionAll β< ¬β)

This comment has been minimized.

Copy link
@jiahao

jiahao Nov 17, 2014

Member

Castagna and Frish's "A Gentle Introduction to Semantic Subtyping" (pdf), which underlies the recent papers we've been reading on set-theoretic types, use the negation type to define their so-called semantic subtyping rule:

screen shot 2014-11-16 at 11 12 42 pm

(s is a subtype of t iff the meet of s and negation-of-t is identical to the bottom type)

It's not exactly clear to me if Castagna's notion of negation type (derived from set complements) is the same as Pierce's notion of negative type (the type we have here), but might be worth testing.

cc: @jakebolewski

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Nov 17, 2014

Author Member

I'm pretty sure this is not a set complement, just a syntactic construct used in the proof.

This comment has been minimized.

Copy link
@jiahao

jiahao Nov 17, 2014

Member

Yes, I understand that. The question is whether the two definitions turn out to be equivalent in the end (for some definition of "equivalent" and "in the end").

The original semantic subtyping paper (pdf) gives this rather opaque definition for the negation type

screen shot 2014-11-16 at 11 35 37 pm

which I haven't grokked yet.

This comment has been minimized.

Copy link
@jiahao

jiahao Nov 17, 2014

Member

The discussion in Pierce, 2002, p. 429 also suggests that the notation is indicative of logical negation, even though he introduced it in a purely formal, syntactic sense. He writes that Curry-Howard correspondence on arrow types S → T encodes the logical proposition S ⇒ T (S implies T) which by definition is equivalent to ¬ S ∨ T (not-S or T).

I read this to mean that the bounded quantifier ∀ X<:S.X is one possible encoding of the logical negation statement ¬ S.

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Nov 17, 2014

Author Member

Ah yes. It is true that this "flips" the subtype relation the same way complement does.

This comment has been minimized.

Copy link
@toivoh

toivoh Nov 17, 2014

Contributor

Why not just say that S is a subtype of T if the meet of S and T is S? Should be equivalent and doesn't require to introduce negation types.

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Nov 17, 2014

Author Member

My guess is the authors just wanted to express the problem as testing for emptiness, which more readily leads to an algorithm for DNF formulas.

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Nov 17, 2014

Author Member

It occurs to me that there are two kinds of bounds on type variables: universal and existential. In A <: B, the relation must hold for all values of variables from A, but there only needs to exist a value for each variable in B. As we work we need to widen bounds on A variables and narrow bounds on B variables. @jiahao I think this corresponds to what you did on the whiteboard, keeping track of which variables are "for all", or "left side" variables.

This comment has been minimized.

Copy link
@jiahao

jiahao Nov 19, 2014

Member

^ Essentially my understanding as well, and what I was describing as 1:1 correspondence between A and B.


a0 = Var(:a0)
env = Dict{Any,Any}(a0 => Any[SubConstraint(θ)])

issub(a0, (@UnionAll a1<:a0 ¬a1), env, false)
end

using Base.Test

issub_strict(x,y) = issub(x,y) && !issub(y,x)

# level 1: no varags, union, UnionAll
function test_1()
@test issub_strict(Int, Integer)
@test issub_strict(Array{Int,1}, AbstractArray{Int,1})

@test isequal_type(Int, Int)
@test isequal_type(Integer, Integer)
@test isequal_type(Array{Int,1}, Array{Int,1})
@test isequal_type(AbstractArray{Int,1}, AbstractArray{Int,1})

@test issub_strict((Int,Int), (Integer,Integer))
@test issub_strict((Array{Int,1},), (AbstractArray{Int,1},))

@test isequal_type((Integer,Integer), (Integer,Integer))

@test !issub((Int,Int), (Int,))
@test !issub((Int,), (Integer,Integer))
end

# level 2: varargs
function test_2()
@test issub_strict((Int,Int), (Int...,))
@test issub_strict((Int,Int), (Int,Int...,))
@test issub_strict((Int,Int), (Int,Integer...,))
@test issub_strict((Int,Int), (Int,Int,Integer...,))
@test issub_strict((Int,Int...), (Int...,))
@test issub_strict((Int,Int,Int...), (Int...,))
@test issub_strict((Int,Int,Int...), (Integer,Int...,))
@test issub_strict((Int...,), (Any...,))
@test issub_strict((), (Any...,))

@test isequal_type((Int...,), (Int...,))
@test isequal_type((Integer...,), (Integer...,))
end

0 comments on commit 23b6a1f

Please sign in to comment.