From 2853d7880b7dd8b2cbc5a621f74cdb8a50abd6c7 Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Wed, 24 Jun 2015 17:14:41 -0400 Subject: [PATCH 1/7] Doing the transition Tuple{...} -> Trait{Tuple{...}} --- src/Traits.jl | 144 +++++++++++++++++++---------------- src/traitdef.jl | 6 +- src/traitfns.jl | 2 +- test/manual-traitdef.jl | 18 +++-- test/manual-traitdispatch.jl | 36 ++++----- test/traitdef.jl | 16 ++-- 6 files changed, 118 insertions(+), 104 deletions(-) diff --git a/src/Traits.jl b/src/Traits.jl index 4dff6e3..89879df 100644 --- a/src/Traits.jl +++ b/src/Traits.jl @@ -8,9 +8,13 @@ module Traits - they are structural types: i.e. they needn't be declared explicitly """ -> current_module() +using SimpleTraits +import SimpleTraits: istrait, Trait + export istrait, istraittype, issubtrait, check_return_types, traitgetsuper, traitgetpara, traitmethods, - @traitdef, @traitimpl, @traitfn, TraitException, All + @traitdef, @traitimpl, @traitfn, TraitException, Not, + Trait if !(VERSION>v"0.4-") error("Traits.jl needs Julia version 0.4.-") @@ -44,11 +48,12 @@ end ####### # Types ####### -@doc """`abstract Trait{SUPER}` +@doc """ +`abstract Trait{SUPER}` from SimpleTraits - All traits are direct decedents of abstract type Trait. The type parameter - SUPER of Trait is needed to specify super-traits (a tuple).""" -> -abstract Trait{SUPER} +All traits are direct decedents of abstract type Trait. The type parameter +SUPER of Trait is needed to specify super-traits (a tuple).""" -> Trait +#abstract Trait{SUPER} # Type of methods field of concrete traits: typealias FDict Dict{Union(Function,DataType),Function} @@ -65,24 +70,14 @@ typealias FDict Dict{Union(Function,DataType),Function} # # where methods field holds the function signatures, like so: # Dict{Function,Any} with 3 entries: -# start => _start(Int64) = Tuple{Vararg{Any}} -# next => _next(Int64,Any) = Tuple{Vararg{Any}} -# done => _done(Int64,Any) = Bool +# start => _start(ret-type, Int64) = nothing +# next => _next(ret-type, Int64,Any) = nothing +# done => _done(ret-type, Int64,Any) = nothing # used to dispatch to helper methods immutable _TraitDispatch end immutable _TraitStorage end -# @doc """Type All is to denote that any type goes in type signatures in -# @traitdef. This is a bit awkward: - -# - method_exists(f, s) returns true if there is a method of f with -# signature sig such that s<:sig. Thus All<->Union() -# - Base.return_types works the other way around, there All<->Any - -# See also https://github.com/JuliaLang/julia/issues/8974"""-> -# abstract All - # General trait exception type TraitException <: Exception msg::String @@ -109,21 +104,34 @@ function istraittype{T<:Tuple}(xs::Type{T}) return true end -@doc """Tests whether a set of types fulfill a trait. - A Trait Tr is defined for some parameters if: +@doc """ +Tests whether a set of types fulfill a trait. +A Trait Tr is defined for some parameters if: - - all the functions of a trait are defined for them - - all the trait constraints are fulfilled +- all the functions of a trait are defined for them +- all the trait constraints are fulfilled - Example: +Example: - `istrait(Tr{Int, Float64})` +`istrait(Tr{Int, Float64})` - or with a tuple of traits: +or with a tuple of traits: - `istrait( (Tr1{Int, Float64}, Tr2{Int}) )` - """ -> +`istrait( (Tr1{Int, Float64}, Tr2{Int}) )` +""" -> +function istrait{T<:Not}(Tr::Type{T}; verbose=false) + Tr = SimpleTraits.stripNot(Tr) + return Tr<:Not ? !(istrait(Tr.parameters[1]; verbose=false)) : istrait(Tr) +end function istrait{T<:Trait}(Tr::Type{T}; verbose=false) + # if it is Trait{Tuple{...}}: + if istraittuple(Tr) + for T in Tr.parameters[1].parameters + istrait(T; verbose=verbose) || return false + end + return true + end + # If it is just a single trait: if verbose println_verb(x) = println("**** Checking $(deparameterize_type(Tr)): " * x) else @@ -210,13 +218,6 @@ function istrait{T<:Trait}(Tr::Type{T}; verbose=false) end return true end -# check a tuple of traits against a signature -function istrait{T<:Tuple}(Trs::Type{T}; verbose=false) - for Tr in Trs - istrait(Tr; verbose=verbose) || return false - end - return true -end ## Helpers for istrait @@ -543,47 +544,56 @@ find_tvar(arg, tv) = Int[] # Sub and supertraits: ###################### @doc """Returns the super traits""" -> -traitgetsuper{T<:Trait}(t::Type{T}) = t.super.parameters[1] -traitgetpara{T<:Trait}(t::Type{T}) = Tuple{t.parameters...} - -@doc """Checks whether a trait, or a tuple of them, is a subtrait of - the second argument.""" -> -function issubtrait{T1<:Trait,T2<:Trait}(t1::Type{T1}, t2::Type{T2}) - if t1==t2 - return true - end - if t2 in traitgetsuper(t1) - return true - end - for t in traitgetsuper(t1) - issubtrait(t, t2) && return true - end - return false -end - -# TODO: think about how to handle tuple traits and empty traits -function issubtrait{T1<:Trait, Tu<:Tuple}(t1::Type{T1}, t2::Type{Tu}) - if t2==() - # the empty trait is the super-trait of all traits - true +function traitgetsuper{T<:Trait}(t::Type{T}) + if istraittuple(t) + t else - throw(TraitException("")) + t.super end end +traitgetpara{T<:Trait}(t::Type{T}) = t.parameters -# traits in a tuple have no order, really, this should reflected. -# Maybe use a set instead? Subtrait if it is a subset? -function issubtrait{T1<:Tuple, T2<:Tuple}(t1::Type{T1}, t2::Type{T2}) - if length(t1)!=length(t2) +@doc """Checks whether a trait, or a tuple of them, is a subtrait of + the second argument.""" -> +function issubtrait{T1<:Trait, T2<:Trait}(t1::Type{T1}, t2::Type{T2}) + if !istraittuple(t1) && istraittuple(t2) + if t2==Trait{Tuple{}} + # the empty trait is the super-trait of all traits + return true + else # TODO + throw(TraitException("")) + end + elseif istraittuple(t1) && istraittuple(t2) + # traits in a tuple have no order, really, this should reflected. + # Maybe use a set instead? Subtrait if it is a subset? + if length(iter(t1))!=length(iter(t2)) + return false + end + checks = true + for (p1,p2) in zip(iter(t1), iter(t2)) + checks = checks && issubtrait(p1,p2) + end + return checks + else + if t1==t2 + return true + end + if t2 in iter(traitgetsuper(t1)) + return true + end + for t in iter(traitgetsuper(t1)) + issubtrait(t, t2) && return true + end return false end - checks = true - for (p1,p2) in zip(t1, t2) - checks = checks && issubtrait(p1,p2) - end - return checks end +# if it is Trait{Tuple{...}} +istraittuple{T<:Trait}(tr::Type{T}) = deparameterize_type(tr)==Trait + +# to iterate over traitgetsuper +iter{T<:Trait}(t::Type{T}) = t.parameters[1] + ## Trait definition include("traitdef.jl") diff --git a/src/traitdef.jl b/src/traitdef.jl index 70d447d..fd087aa 100644 --- a/src/traitdef.jl +++ b/src/traitdef.jl @@ -88,12 +88,10 @@ function parsebody(body::Expr) # end # # into - # - - - + # :(f1 => _f1((X,Int), X, Y)=nothing, ...), # :(Bool[X==Y]), # :(...associated types...) + isassoc(ex::Expr) = ex.head==:(=) # associated types isconstraints(ex::Expr) = ex.head==:macrocall # constraints diff --git a/src/traitfns.jl b/src/traitfns.jl index 372e7c3..b43fefd 100644 --- a/src/traitfns.jl +++ b/src/traitfns.jl @@ -244,7 +244,7 @@ end function traitdispatch(traittypes, fname) poss = Any[] for Tr in traittypes - if istrait(Tr) + if istrait(Trait{Tr}) push!(poss, Tr) end end diff --git a/test/manual-traitdef.jl b/test/manual-traitdef.jl index f3093a9..a6edd34 100644 --- a/test/manual-traitdef.jl +++ b/test/manual-traitdef.jl @@ -3,7 +3,8 @@ # All type belong to the empty trait, as it makes no restriction on # the types: -@test istrait( Tuple{} ) +@test istrait( Trait{Tuple{}} ) +#@test !istrait( Not{Tuple{}} ) immutable Tr1{X1} <: Traits.Trait{Tuple{}} methods::Traits.FDict @@ -28,10 +29,10 @@ end @test istraittype(Tr1{A1}) @test istraittype( Tuple{Tr1{A1},Tr2{A1,A2}} ) -@test traitgetpara(Tr1{A1})==Tuple{A1} -@test traitgetsuper(Tr1{A1})==Tuple{} -@test traitgetsuper(Tr2{A1,A2})==Tuple{} -@test traitgetsuper(Tr3{A1,A2})==Tuple{Tr1{A1},Tr2{A1,A2}} +@test traitgetpara(Tr1{A1})==Base.svec(A1) +@test traitgetsuper(Tr1{A1})==Trait{Tuple{}} +@test traitgetsuper(Tr2{A1,A2})==Trait{Tuple{}} +@test traitgetsuper(Tr3{A1,A2})==Trait{Tuple{Tr1{A1},Tr2{A1,A2}}} # any type is part of a unconstrained trait: @test istrait(Tr1{Int}, verbose=verbose) @@ -39,6 +40,11 @@ end @test istrait(Tr3{String,DataType}) @test_throws TraitException istrait(Tr3{:a,7}) # maybe this should error? +@test !istrait(Not{Tr1{Int}}, verbose=verbose) +@test !istrait(Not{Tr2{DataType,Int}}) +@test !istrait(Not{Tr3{String,DataType}}) + + immutable D1{X1} <: Traits.Trait{Tuple{}} methods::Traits.FDict constraints::Vector{Bool} @@ -108,7 +114,7 @@ end @test istrait(D3{Dict{Int,Int}}) @test !istrait(D3{Int}) -@test istrait(Tuple{D1{Int}, D2{Int, Int}} ) +@test istrait(Trait{Tuple{D1{Int}, D2{Int, Int}}} ) @test istrait(D4{Int,FloatingPoint}) diff --git a/test/manual-traitdispatch.jl b/test/manual-traitdispatch.jl index d299b2f..b8c2644 100644 --- a/test/manual-traitdispatch.jl +++ b/test/manual-traitdispatch.jl @@ -16,16 +16,16 @@ f2{X,Y<:FloatingPoint}(x::X, y::Y) = f2(x, y, f2(TraitType(), x,y) ) # so that it doesn't occupy f2(x::Any, y::Any) # the logic is: -@inline f2{X,Y<:Integer}(x::X, y::Y, ::Type{ Tuple{D1{Y}, D4{X,Y}} }) = x + sin(y) -@inline f2{S,T<:Integer}(s::S, t::T, ::Type{ Tuple{D1{S}, D1{T}} }) = sin(s) - sin(t) -@inline f2{X,Y<:FloatingPoint}(x::X, y::Y, ::Type{ Tuple{D1{X}, D1{Y}} }) = cos(x) - cos(y) +@inline f2{X,Y<:Integer}(x::X, y::Y, ::Type{ Trait{Tuple{D1{Y}, D4{X,Y}}} }) = x + sin(y) +@inline f2{S,T<:Integer}(s::S, t::T, ::Type{ Trait{Tuple{D1{S}, D1{T}}} }) = sin(s) - sin(t) +@inline f2{X,Y<:FloatingPoint}(x::X, y::Y, ::Type{ Trait{Tuple{D1{X}, D1{Y}}} }) = cos(x) - cos(y) # the trait dispatch. Here I rename all the arguments and type parameters. function f2{X1,X2<:FloatingPoint}(::TraitType, ::Type{X1}, ::Type{X2}) # This method serves as a storage for the trait-types of a certain # normal-type signature. - [ Tuple{D1{X1}, D1{X2}} ], Any[:(Tuple{D1{X1}, D1{X2}})] + [ Trait{Tuple{D1{X1}, D1{X2}}} ], Any[:(Trait{Tuple{D1{X1}, D1{X2}}})] end @generated function f2{X1,X2<:FloatingPoint}(::TraitType, x1::X1, x2::X2) traittypes = f2(TraitType(), x1, x2)[1] @@ -43,15 +43,15 @@ end end # construct function from poss[1] out = :(Tuple{}) - for s in poss[1] + for s in poss[1].parameters[1] push!(out.args, :($s)) end - return out + return :(Trait{$out}) end function f2{X1,X2<:Integer}(::TraitType, ::Type{X1}, ::Type{X2}) - [Tuple{D1{X2}, D4{X1,X2}}, Tuple{D1{X1}, D1{X2}} ], Any[:(Tuple{D1{X2}, D4{X1,X2}}), :(Tuple{D1{X1}, D1{X2}})] + [Trait{Tuple{D1{X2}, D4{X1,X2}}}, Trait{Tuple{D1{X1}, D1{X2}}} ], Any[:(Trait{Tuple{D1{X2}, D4{X1,X2}}}), :(Trait{Tuple{D1{X1}, D1{X2}}})] end @generated function f2{X1,X2<:Integer}(::TraitType, x1::X1, x2::X2) traittypes = f2(TraitType(), x1, x2)[1] @@ -69,10 +69,10 @@ end end out = :(Tuple{}) - for s in poss[1] + for s in poss[1].parameters[1] push!(out.args, :($s)) end - return out + return :(Trait{$out}) end # @traitfn f2{X,Y<:Integer; D1{Y}, D4{X,Y}}(x::X,y::Y) = sin(x) + y @@ -121,16 +121,16 @@ f1{X,Y<:FloatingPoint}(x::X, y::Y) = _trait_f1(x, y, _trait_type_f1(x,y) ) # so that it doesn't occupy f1(x::Any, y::Any) # the logic is: -@inline _trait_f1{X,Y<:Integer}(x::X, y::Y, ::Type{ Tuple{D1{Y}, D4{X,Y}} }) = x + sin(y) -@inline _trait_f1{S,T<:Integer}(s::S, t::T, ::Type{ Tuple{D1{S}, D1{T}} }) = sin(s) - sin(t) -@inline _trait_f1{X,Y<:FloatingPoint}(x::X, y::Y, ::Type{ Tuple{D1{X}, D1{Y}} }) = cos(x) - cos(y) +@inline _trait_f1{X,Y<:Integer}(x::X, y::Y, ::Type{ Trait{Tuple{D1{Y}, D4{X,Y}}} }) = x + sin(y) +@inline _trait_f1{S,T<:Integer}(s::S, t::T, ::Type{ Trait{Tuple{D1{S}, D1{T}}} }) = sin(s) - sin(t) +@inline _trait_f1{X,Y<:FloatingPoint}(x::X, y::Y, ::Type{ Trait{Tuple{D1{X}, D1{Y}}} }) = cos(x) - cos(y) # the trait dispatch. Here I rename all the arguments and type parameters. function _trait_type_f1{X1,X2<:FloatingPoint}(::Type{X1}, ::Type{X2}) # This method serves as a storage for the trait-types of a certain # normal-type signature. - [Tuple{D1{X1}, D1{X2}}], Any[:(Tuple{D1{X1}, D1{X2}})] + [Trait{Tuple{D1{X1}, D1{X2}}}], Any[:(Trait{Tuple{D1{X1}, D1{X2}}})] end @generated function _trait_type_f1{X1,X2<:FloatingPoint}(x1::X1, x2::X2) traittypes = _trait_type_f1(x1, x2)[1] @@ -148,15 +148,15 @@ end end # construct function from poss[1] out = :(Tuple{}) - for s in poss[1] + for s in poss[1].parameters[1] push!(out.args, :($s)) end - return out + return :(Trait{$out}) end function _trait_type_f1{X1,X2<:Integer}(::Type{X1}, ::Type{X2}) - [Tuple{D1{X2}, D4{X1,X2}}, Tuple{D1{X1}, D1{X2}}], Any[:(Tuple{D1{X2}, D4{X1,X2}}), :(Tuple{D1{X1}, D1{X2}})] + [Trait{Tuple{D1{X2}, D4{X1,X2}}}, Trait{Tuple{D1{X1}, D1{X2}}}], Any[:(Trait{Tuple{D1{X2}, D4{X1,X2}}}), :(Trait{Tuple{D1{X1}, D1{X2}}})] end @generated function _trait_type_f1{X1,X2<:Integer}(x1::X1, x2::X2) traittypes = _trait_type_f1(x1, x2)[1] @@ -174,10 +174,10 @@ end end out = :(Tuple{}) - for s in poss[1] + for s in poss[1].parameters[1] push!(out.args, :($s)) end - return out + return :(Trait{$out}) end # @traitfn f1{X,Y<:Integer; D1{Y}, D4{X,Y}}(x::X,y::Y) = sin(x) + y diff --git a/test/traitdef.jl b/test/traitdef.jl index 140652b..acd75cc 100644 --- a/test/traitdef.jl +++ b/test/traitdef.jl @@ -153,9 +153,9 @@ end ==(X,Y) -> Bool end -@test traitgetsuper(Tr20)==Tuple{} -@test traitgetsuper(Tr21)==Tuple{Tr20} -@test traitgetsuper(Tr13)==Tuple{Tr11, Tr20, Tr21} +@test traitgetsuper(Tr20)==Trait{Tuple{}} +@test traitgetsuper(Tr21)==Trait{Tuple{Tr20}} +@test traitgetsuper(Tr13)==Trait{Tuple{Tr11, Tr20, Tr21}} @test issubtrait(Tr21, Tr20) @test issubtrait(Tr211, Tr20) @@ -165,13 +165,13 @@ end @test issubtrait(Tr13, Tr21) @test issubtrait(Tr13, Tr20) -@test issubtrait(Tuple{Tr21}, Tuple{Tr20}) -@test issubtrait(Tuple{Tr21,Tr11}, Tuple{Tr20,Tr10}) -@test !issubtrait(Tuple{Tr21,Tr11}, Tuple{Tr10,Tr20}) # todo: this should be true, as order shouldn't matter -@test issubtrait(Tuple{Tr11,Tr21}, Tuple{Tr10,Tr20}) +@test issubtrait(Trait{Tuple{Tr21}}, Trait{Tuple{Tr20}}) +@test issubtrait(Trait{Tuple{Tr21,Tr11}}, Trait{Tuple{Tr20,Tr10}}) +@test !issubtrait(Trait{Tuple{Tr21,Tr11}}, Trait{Tuple{Tr10,Tr20}}) # todo: this should be true, as order shouldn't matter +@test issubtrait(Trait{Tuple{Tr11,Tr21}}, Trait{Tuple{Tr10,Tr20}}) @test !issubtrait(Tr21{Int}, Tr20{Float64}) -@test !issubtrait(Tuple{Tr21{Int}}, Tuple{Tr20{Float64}}) +@test !issubtrait(Trait{Tuple{Tr21{Int}}}, Trait{Tuple{Tr20{Float64}}}) #### # Test functions parameterized on non-trait parameters. From 89224e448717d79721dd7ac460464dc2dbd395aa Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Thu, 25 Jun 2015 16:42:09 -0400 Subject: [PATCH 2/7] Now traitfn working with Not --- src/traitfns.jl | 20 +++++++++++++++----- test/traitfns.jl | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/traitfns.jl b/src/traitfns.jl index b43fefd..bd789d5 100644 --- a/src/traitfns.jl +++ b/src/traitfns.jl @@ -40,7 +40,10 @@ function parsetraitfn_head(head::Expr) name = nametyp.args[1] fun = Expr(:curly, deepcopy(nametyp.args[[1;3:end]])...) typs = fun.args[2:end] - traits = nametyp.args[2].args + traits = Any[] + for t in nametyp.args[2].args + push!(traits, SimpleTraits.isnegated(t) ? :(Not{$(t.args[2])}) : t) + end return ParsedFn(name, fun, typs, sig, traits, :()) end @@ -48,7 +51,7 @@ gettypesymbol(x::Expr) = x.args[1] # :(X1<:Int) gettypesymbol(x::Symbol) = x function translate_head(fn::ParsedFn) # Takes output from parsetraitfn_head and - # renames sig and TypeVar: + # renames sig and TypeVar and translates !-> Not: # f1{X,Y; D1{X}, D2{X,Y}}(x::X,y::Y) # -> # f1{X1,X2; D1{X1}, D2{X1,X2}}(x::X1,y::X2) @@ -109,7 +112,11 @@ function translate_head(fn::ParsedFn) end end for t in fnt.traits - t.args[2:end] = map(x->trans_Tvar[x], t.args[2:end]) + if t.args[1]==:Not + t.args[2].args[2:end] = map(x->trans_Tvar[x], t.args[2].args[2:end]) + else + t.args[2:end] = map(x->trans_Tvar[x], t.args[2:end]) + end end return fnt @@ -311,7 +318,7 @@ end - trait-methods and non-trait methods can be mixed. - for details on trait dispatch see doc of Traits.traitdispatch """ -> -macro traitfn(fndef) +function traitfn(fndef) fn, fnt = parsetraitfn(fndef) if length(unique(fnt.traits))!=length(fnt.traits) throw(TraitException( @@ -411,7 +418,10 @@ macro traitfn(fndef) $trait_type_f $f end - return esc(out) + return out +end +macro traitfn(def) + esc(traitfn(def)) end ########## diff --git a/test/traitfns.jl b/test/traitfns.jl index a855643..05cebc5 100644 --- a/test/traitfns.jl +++ b/test/traitfns.jl @@ -244,3 +244,33 @@ using Base.Test @test length(traitmethods(ff879))==1 end @test length(traitmethods(ff879))==1 + + +########### +# Not +###### +@traitdef Pr333{X} begin + fn786{T<:X}(T,T) +end +fn786(b::Int, c::Int) = b +@test istrait(Pr333{Int}) +@test !istrait(Pr333{Real}) + +@traitfn ff875{T; Pr333{T} }(x::T) = 2x +@traitfn ff875{T; Not{Pr333{T}}}(x::T) = 2000x +@traitfn ff875{T; !Pr333{T} }(x::T) = 2000x +@test ff875(5)==10 +@test ff875(5.)==2000*5. + + +@traitdef Pr334{X} begin + fn788{T<:X}(T,T) +end +fn788(b::Int, c::Int) = b +@test istrait(Pr334{Int}) +@test !istrait(Pr334{Real}) + +@traitfn ff876{T; Pr333{T}, !Pr334{T} }(x::T) = 2x +@traitfn ff876{T; !Pr333{T}, !Pr334{T} }(x::T) = 2000x +@test_throws TraitException ff876(5) +@test ff876(5.0)==2000*5.0 From 6e548e73566e0bbc1ff870a776ffdbbd8f70c397 Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Sat, 27 Jun 2015 16:01:08 -0400 Subject: [PATCH 3/7] Trait{Tuple} working in traitfn --- src/Traits.jl | 19 +++++++++++++------ src/traitfns.jl | 18 +++++++++++++++++- test/traitdef.jl | 12 ++++++++---- test/traitfns.jl | 12 +++++++++++- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/Traits.jl b/src/Traits.jl index 89879df..cd3534f 100644 --- a/src/Traits.jl +++ b/src/Traits.jl @@ -566,11 +566,11 @@ function issubtrait{T1<:Trait, T2<:Trait}(t1::Type{T1}, t2::Type{T2}) elseif istraittuple(t1) && istraittuple(t2) # traits in a tuple have no order, really, this should reflected. # Maybe use a set instead? Subtrait if it is a subset? - if length(iter(t1))!=length(iter(t2)) + if length(itertraittuple(t1))!=length(itertraittuple(t2)) return false end checks = true - for (p1,p2) in zip(iter(t1), iter(t2)) + for (p1,p2) in zip(itertraittuple(t1), itertraittuple(t2)) checks = checks && issubtrait(p1,p2) end return checks @@ -578,10 +578,10 @@ function issubtrait{T1<:Trait, T2<:Trait}(t1::Type{T1}, t2::Type{T2}) if t1==t2 return true end - if t2 in iter(traitgetsuper(t1)) + if t2 in itertraittuple(traitgetsuper(t1)) return true end - for t in iter(traitgetsuper(t1)) + for t in itertraittuple(traitgetsuper(t1)) issubtrait(t, t2) && return true end return false @@ -589,10 +589,17 @@ function issubtrait{T1<:Trait, T2<:Trait}(t1::Type{T1}, t2::Type{T2}) end # if it is Trait{Tuple{...}} -istraittuple{T<:Trait}(tr::Type{T}) = deparameterize_type(tr)==Trait +function istraittuple{T<:Trait}(tr::Type{T}) + deparameterize_type(tr)!=Trait && return false + if length(tr.parameters)==1 && tr.parameters[1]<:Tuple + return true + else + return false + end +end # to iterate over traitgetsuper -iter{T<:Trait}(t::Type{T}) = t.parameters[1] +itertraittuple{T<:Trait}(t::Type{T}) = t.parameters[1] ## Trait definition include("traitdef.jl") diff --git a/src/traitfns.jl b/src/traitfns.jl index bd789d5..8378f9e 100644 --- a/src/traitfns.jl +++ b/src/traitfns.jl @@ -29,10 +29,20 @@ type ParsedFn # (probably should adapt MetaTools.jl...) end # Parsing: +addit!(traits, t) = push!(traits, SimpleTraits.isnegated(t) ? :(Not{$(t.args[2])}) : t) +function istraittuple(t::Expr) + tr = isa(t.args[1],Symbol) ? t.args[1] : t.args[1].args[2] + tr!=:Trait && return false + tu = t.args[2].args[1] + return tu==:Tuple ? true : false +end +itertraittuple(t::Expr) = t.args[2].args[2:end] function parsetraitfn_head(head::Expr) # Transforms # f1{X<:Int,Y; D1{X}, D2{X,Y}}(x::X,y::Y) # + # f1{X<:Int,Y; Trait{Tuple{D1{X}, D2{X,Y}}}}(x::X,y::Y) + # # into a ParsedFn nametyp = head.args[1] @@ -42,7 +52,13 @@ function parsetraitfn_head(head::Expr) typs = fun.args[2:end] traits = Any[] for t in nametyp.args[2].args - push!(traits, SimpleTraits.isnegated(t) ? :(Not{$(t.args[2])}) : t) + if istraittuple(t) + for tt in itertraittuple(t) + addit!(traits, tt) + end + else + addit!(traits, t) + end end return ParsedFn(name, fun, typs, sig, traits, :()) end diff --git a/test/traitdef.jl b/test/traitdef.jl index acd75cc..9edbb7b 100644 --- a/test/traitdef.jl +++ b/test/traitdef.jl @@ -130,16 +130,16 @@ f948576() = 1 @test istrait(FF{Int}) @traitdef Tr20{X} begin - length(X) -> Bool + length(X) -> Int end @traitdef Tr21{X} <: Tr20{X} begin - size(X) -> Bool + size(X) -> Int end @traitdef Tr211{X} <: Tr21{X} begin - size(X) -> Bool + size(X) -> Int end @traitdef Tr2111{X} <: Tr211{X} begin - size(X) -> Bool + size(X) -> Int end @traitdef Tr10{X,Y} begin @@ -173,6 +173,10 @@ end @test !issubtrait(Tr21{Int}, Tr20{Float64}) @test !issubtrait(Trait{Tuple{Tr21{Int}}}, Trait{Tuple{Tr20{Float64}}}) +# istrait +@test istrait(Tr13{Int,Int})==istrait(Trait{Tuple{Tr13{Int,Int}}}) +@test istrait(Tr20{Int})==istrait(Trait{Tuple{Tr20{Int}}}) + #### # Test functions parameterized on non-trait parameters. ### diff --git a/test/traitfns.jl b/test/traitfns.jl index 05cebc5..f15123a 100644 --- a/test/traitfns.jl +++ b/test/traitfns.jl @@ -246,7 +246,17 @@ end @test length(traitmethods(ff879))==1 -########### +##### +# Tuples +##### +@traitdef Pr300{X} begin end +Traits.istrait(::Type{Pr300{Int64}}) = true +Traits.istrait(::Type{Pr300{Int32}}) = true +@traitfn ff555{X,Y; Trait{Tuple{Pr300{X}, Pr300{Y}}}}(x::X,y::Y) = 1 +@test ff555(4,Int32(5))==1 +@test_throws TraitException ff555(4,5) + +###### # Not ###### @traitdef Pr333{X} begin From e45c8cd856a219d47a699eb8b2b7916b5e95fee0 Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Sun, 28 Jun 2015 12:42:43 -0400 Subject: [PATCH 4/7] Probably a Julia-dispatch bug remaining See dispatch_bug1 in runtest.jl --- src/traitdef.jl | 89 ++++++++++++++++++++++++++++-------------------- test/runtests.jl | 2 ++ test/traitfns.jl | 16 ++++++--- 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/traitdef.jl b/src/traitdef.jl index fd087aa..1883c6a 100644 --- a/src/traitdef.jl +++ b/src/traitdef.jl @@ -221,49 +221,64 @@ end # 3) piece it together ### -@doc """The `@traitdef` macro is used to construct a trait. Example: - - ``` - @traitdef MyArith{X,Y} begin - # associated types - Z = promote_type(X,Y) - D = (X,Y)<:(Integer, Integer) ? Float64 : Z - - # method signatures - +(X,Y) -> Z +@doc """ +The `@traitdef` macro is used to construct a trait. Example: + +``` +@traitdef MyArith{X,Y} begin + # associated types + Z = promote_type(X,Y) + D = (X,Y)<:(Integer, Integer) ? Float64 : Z + + # method signatures + +(X,Y) -> Z -(X,Y) -> Z - *(X,Y) -> Z - /(X,Y) -> D + *(X,Y) -> Z + /(X,Y) -> D + + # constraints on X,Y + @constraints begin + X<:Number + Y<:Number + end +end +istrait(MyArith{Int, Int8}) # -> true +``` + +- Assignments are for associated types, here `Z,D`. These are + types which can be calculated from the input types `X,Y` - # constraints on X,Y - @constraints begin - X<:Number - Y<:Number - end - end - istrait(MyArith{Int, Int8}) # -> true - ``` - - - Assignments are for associated types, here `Z,D`. These are - types which can be calculated from the input types `X,Y` +- Function signature definitions which are of the form `fn(X,Y, + Other-Types) -> Return-Types`. The return types can be left away - - Function signature definitions which are of the form `fn(X,Y, - Other-Types) -> Return-Types`. The return types can be left away +- Constraints are marked in a block `@constaints`. The are + constraints in terms of the input types `X,Y` and are evaluated + at trait checking. - - Constraints are marked in a block `@constaints`. The are - constraints in terms of the input types `X,Y` and are evaluated - at trait checking. +Traits can subtrait others: - Traits can subtrait others: +``` +@traitdef MyInv{X}<:MyArith{X,X} begin + inv(X) -> X +end +istrait(MyInv{Int}) # -> false +istrait(MyInv{Float64}) # -> true +``` - ``` - @traitdef MyInv{X}<:MyArith{X,X} begin - inv(X) -> X - end - istrait(MyInv{Int}) # -> false - istrait(MyInv{Float64}) # -> true - ``` - """ -> +Traits can be constructed compatible to SimpleTraits.jl without +begin-end: +``` +@traitdef MyInv{X}<:MyArith{X,X} +``` +which is equivalent to +@traitdef MyInv{X}<:MyArith{X,X} begin + @constraints begin + false + end +end +``` +i.e. a trait which contains no types. +""" -> macro traitdef(head, body) ## make Trait type traithead, name = parsetraithead(head) diff --git a/test/runtests.jl b/test/runtests.jl index fc783e0..5ceaf04 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,6 +8,8 @@ method_exists_bug1 = false # see https://github.com/JuliaLang/julia/issues/8959 method_exists_bug2 = false # see https://github.com/JuliaLang/julia/issues/9043 and https://github.com/mauro3/Traits.jl/issues/2 # these two are not relevant anymore as method_exists is not used anymore +dispatch_bug1 = true # not filed yet + return_types_bug1 = true # see Pr0 in traitdef.jl function_types_bug1 = true # set to false if function types get implemented in Julia diff --git a/test/traitfns.jl b/test/traitfns.jl index f15123a..e7bbb3a 100644 --- a/test/traitfns.jl +++ b/test/traitfns.jl @@ -249,12 +249,20 @@ end ##### # Tuples ##### -@traitdef Pr300{X} begin end +@traitdef Pr300{X} begin + @constraints begin + false==true + end +end Traits.istrait(::Type{Pr300{Int64}}) = true Traits.istrait(::Type{Pr300{Int32}}) = true -@traitfn ff555{X,Y; Trait{Tuple{Pr300{X}, Pr300{Y}}}}(x::X,y::Y) = 1 -@test ff555(4,Int32(5))==1 -@test_throws TraitException ff555(4,5) +if !dispatch_bug1 + @test istrait(Trait{Tuple{Pr300{Int64}, Pr300{Int32}}}) + @test istrait(Trait{Tuple{Pr300{Int64}, Pr300{Int64}}}) + @traitfn ff555{X,Y; Trait{Tuple{Pr300{X}, Pr300{Y}}}}(x::X,y::Y) = 1 + @test ff555(4,Int32(5))==1 + @test_throws TraitException ff555(4,5.0) +end ###### # Not From ca4e81fdd7a98531b0e77940e2e58b408fba7d52 Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Sun, 28 Jun 2015 15:24:27 -0400 Subject: [PATCH 5/7] small stuff. TraitException in SimpleTraits --- src/Traits.jl | 5 ----- test/runtests.jl | 2 +- test/traitdispatch.jl | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Traits.jl b/src/Traits.jl index cd3534f..9c8e71c 100644 --- a/src/Traits.jl +++ b/src/Traits.jl @@ -78,11 +78,6 @@ typealias FDict Dict{Union(Function,DataType),Function} immutable _TraitDispatch end immutable _TraitStorage end -# General trait exception -type TraitException <: Exception - msg::String -end - # Helper dummy types used in istrait below abstract _TestType{T} immutable _TestTvar{T}<:_TestType{T} end # used for TypeVar testing diff --git a/test/runtests.jl b/test/runtests.jl index 5ceaf04..f8c6cb1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,7 +8,7 @@ method_exists_bug1 = false # see https://github.com/JuliaLang/julia/issues/8959 method_exists_bug2 = false # see https://github.com/JuliaLang/julia/issues/9043 and https://github.com/mauro3/Traits.jl/issues/2 # these two are not relevant anymore as method_exists is not used anymore -dispatch_bug1 = true # not filed yet +dispatch_bug1 = true # not filed yet, maybe related https://github.com/JuliaLang/julia/issues/11803 return_types_bug1 = true # see Pr0 in traitdef.jl diff --git a/test/traitdispatch.jl b/test/traitdispatch.jl index 4dcc5da..1296a96 100644 --- a/test/traitdispatch.jl +++ b/test/traitdispatch.jl @@ -142,7 +142,6 @@ end @test tf7465(5,6)==5*6 @traitdef TrTr22{X,Y} <: TrTr1{X}, TrTr1{Y} begin - # empty end @traitfn tf7465{X<:Integer,Y; TrTr22{X,Y}}(x::X,y::Y) = x*y*1000 From 84c3f0b9468181baa252077b7b3086bad74cde53 Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Sun, 28 Jun 2015 17:53:17 -0400 Subject: [PATCH 6/7] Yep, working, seems compatible with SimpleTraits --- src/Traits.jl | 84 +++++++++++++++++++++++++++-------------- src/traitdef.jl | 5 +++ src/traitfns.jl | 57 +++++++++++++++++++--------- src/traitimpl.jl | 9 ++++- test/manual-traitdef.jl | 6 +-- test/runtests.jl | 4 +- test/traitdef.jl | 28 +++++++------- test/traitdispatch.jl | 16 ++++---- test/traitfns.jl | 4 +- 9 files changed, 138 insertions(+), 75 deletions(-) diff --git a/src/Traits.jl b/src/Traits.jl index 9c8e71c..cf682ef 100644 --- a/src/Traits.jl +++ b/src/Traits.jl @@ -9,17 +9,22 @@ module Traits """ -> current_module() using SimpleTraits -import SimpleTraits: istrait, Trait +import SimpleTraits: istrait, trait, Trait export istrait, istraittype, issubtrait, check_return_types, traitgetsuper, traitgetpara, traitmethods, - @traitdef, @traitimpl, @traitfn, TraitException, Not, - Trait + @trait, @traitdef, @traitimpl, @traitfn, Not, Trait, + TraitException, TraitMethodError if !(VERSION>v"0.4-") error("Traits.jl needs Julia version 0.4.-") end +# If no trait method matches +type TraitMethodError <: Exception + msg::String +end + ## patches for bugs in base include("base_fixes.jl") @@ -45,6 +50,15 @@ function check_return_types(flg::Bool) flag_check_return_types = flg end +const verbose=false +@doc """ +Turn on verbose error messages +"""-> +function verbosity(val::Bool) + global verbose + verbose = val +end + ####### # Types ####### @@ -113,31 +127,44 @@ Example: or with a tuple of traits: `istrait( (Tr1{Int, Float64}, Tr2{Int}) )` -""" -> -function istrait{T<:Not}(Tr::Type{T}; verbose=false) - Tr = SimpleTraits.stripNot(Tr) - return Tr<:Not ? !(istrait(Tr.parameters[1]; verbose=false)) : istrait(Tr) +""" -> istrait + +# for debug info +function println_verb(x) + if verbose + println("**** Checking $(deparameterize_type(Tr)): " * x) + end + nothing end -function istrait{T<:Trait}(Tr::Type{T}; verbose=false) + +# function trait{T<:Not}(Tr::Type{T}) +# Tr = SimpleTraits.stripNot(Tr) +# return Tr<:Not ? !(trait(Tr.parameters[1])) : trait(Tr) +# end +function trait{T<:Trait}(Tr::Type{T}) + # To avoid nested Not's: + Tr = SimpleTraits.stripNot(Tr) + NotTr = SimpleTraits.stripNot(Not{Tr}) + + if traitgetsuper(Tr).parameters[1]==TypeVar(:S) + # this is a SimpleTrait and if it dispatches to here it is false + return NotTr + end + # if it is Trait{Tuple{...}}: if istraittuple(Tr) for T in Tr.parameters[1].parameters - istrait(T; verbose=verbose) || return false + istrait(T) || return false end - return true + return Tr end # If it is just a single trait: - if verbose - println_verb(x) = println("**** Checking $(deparameterize_type(Tr)): " * x) - else - println_verb = x->x - end if !hasparameters(Tr) throw(TraitException("Trait $Tr has no type parameters.")) end # check supertraits - !istrait(traitgetsuper(Tr); verbose=verbose) && return false + !istrait(traitgetsuper(Tr)) && return NotTr # check instantiating tr = nothing @@ -148,13 +175,13 @@ function istrait{T<:Trait}(Tr::Type{T}; verbose=false) This usually indicates that something is amiss with the @traitdef or that one of the generic functions is not defined. The error was: $err""") - return false + return NotTr end # check constraints if !all(tr.constraints) println_verb("Not all constraints are satisfied for $T") - return false + return NotTr end # Check call signature of all methods: @@ -167,7 +194,7 @@ function istrait{T<:Trait}(Tr::Type{T}; verbose=false) checks = false # Only loop over methods which have the right number of arguments: for fm in methods(gf, NTuple{length(tm.sig),Any}) - if isfitting(tmm, fm, verbose=verbose) + if isfitting(tmm, fm) checks = true break end @@ -175,7 +202,7 @@ function istrait{T<:Trait}(Tr::Type{T}; verbose=false) if !checks # if check==false no fitting method was found println_verb("""No method of the generic function/call-overloaded `$gf` matched the trait specification: `$tm`""") - return false + return NotTr end end end @@ -206,12 +233,12 @@ function istrait{T<:Trait}(Tr::Type{T}; verbose=false) $fret_typ Returning false. """) - return false + return NotTr end end end end - return true + return Tr end ## Helpers for istrait @@ -264,8 +291,7 @@ end {T<:Integer}(T, T, Integer) <<: {T<:Integer}(T, T, T) -> false as parametric constraints are not equal """ -> -function isfitting(tmm::Method, fmm::Method; verbose=false) # tm=trait-method, fm=function-method - println_verb = verbose ? println : x->x # TODO maybe move this function out +function isfitting(tmm::Method, fmm::Method) # tm=trait-method, fm=function-method tm = FakeMethod(tmm, ret=true) fm = FakeMethod(fmm) @@ -390,7 +416,7 @@ function isfitting(tmm::Method, fmm::Method; verbose=false) # tm=trait-method, f # g01{T<:X}(T, T) -> T # end # g01(::Int, ::Int) = Int - # @assert istrait(Tr01{Int}, verbose=true) + # @assert istrait(Tr01{Int}) if isleaftype(tv.ub) # note isleaftype can have some issues with inner constructors # Check if the method definition of fm has the same # leaftypes in the same location. @@ -543,17 +569,19 @@ function traitgetsuper{T<:Trait}(t::Type{T}) if istraittuple(t) t else - t.super + t.super==SimpleTraits.Trait ? SimpleTraits.Trait{Tuple{}} : t.super end end traitgetpara{T<:Trait}(t::Type{T}) = t.parameters -@doc """Checks whether a trait, or a tuple of them, is a subtrait of - the second argument.""" -> +@doc """ +Checks whether a trait, or a tuple of them, is a subtrait of +the second argument.""" -> function issubtrait{T1<:Trait, T2<:Trait}(t1::Type{T1}, t2::Type{T2}) if !istraittuple(t1) && istraittuple(t2) if t2==Trait{Tuple{}} # the empty trait is the super-trait of all traits + # TODO maybe update that. return true else # TODO throw(TraitException("")) diff --git a/src/traitdef.jl b/src/traitdef.jl index 1883c6a..7b792b5 100644 --- a/src/traitdef.jl +++ b/src/traitdef.jl @@ -63,7 +63,12 @@ function parsetraithead(def::Expr) else error("Interface specification error") end + if supertraits==:() + supertraits = :(TypeVar(:SUPER)) + end + # check supertraits<:Traits + # TODO: move this check to runtime for i =2:length(supertraits.args) st = supertraits.args[i].args[1] eval_curmod(:(@assert istraittype($st))) diff --git a/src/traitfns.jl b/src/traitfns.jl index 8378f9e..a723d42 100644 --- a/src/traitfns.jl +++ b/src/traitfns.jl @@ -73,7 +73,7 @@ function translate_head(fn::ParsedFn) # f1{X1,X2; D1{X1}, D2{X1,X2}}(x::X1,y::X2) # # Returns translated ParsedFn - + function make_trans(sig, typs) # makes two dictionaries with keys the old typevars, and # values the lowercase and uppercase variables @@ -82,7 +82,11 @@ function translate_head(fn::ParsedFn) trans_var = Dict{Symbol,Symbol}() for (i,si) in enumerate(GenerateTypeVars{:lcase}()) if length(sig) [:Int, :Any] out = Any[] @@ -198,16 +207,23 @@ function get_concrete_type_symb(typs) push!(out, t.args[2]) end end + # pad with Any + for i=1:(len-length(out)) + push!(out, :Any) + end return out end -function get_concrete_type_Typetuple(typs) +function get_concrete_type_Typetuple(typs, len) # Returns the most general type satisfying typs as a tuple of # actual types: # [:(X<:Int), :Y] -> Tuple{Int, Any} - out = get_concrete_type_symb(typs) + out = get_concrete_type_symb(typs, len) for i in 1:length(out) out[i] = Type{eval_curmod(out[i])} end + for i=1:(len-length(out)) + push!(out, :Any) + end return Tuple{out...} end @@ -215,7 +231,11 @@ function make_Type_sig(typs) # [:(X<:Int), :Y] -> [:(::Type{X}), :(::Type{Y})] out = Any[] for t in typs - push!(out, :(::Type{$t})) + if t==:Any + push!(out, :(::Any)) + else + push!(out, :(::Type{$t})) + end # if isa(t, Symbol) || t.head==:. # else @@ -224,22 +244,22 @@ function make_Type_sig(typs) end return out end -function has_only_one_method(fname::Symbol, typs) +function has_only_one_method(fname::Symbol, typs, len) # Checks whether fname has one and only one method for types in # typs. if isdefined(current_module(), fname) 1 == length( methods(eval_curmod(fname), - get_concrete_type_Typetuple(typs)) ) + get_concrete_type_Typetuple(typs, len)) ) else false end end -function has_only_one_method(fname::Expr, typs) +function has_only_one_method(fname::Expr, typs, len) # Checks whether fname has one and only one method for types in # typs. if isdefined(eval_curmod(fname.args[1]), fname.args[2].args[1]) 1 == length( methods(eval_curmod(fname), - get_concrete_type_Typetuple(typs)) ) + get_concrete_type_Typetuple(typs, len)) ) else false end @@ -307,9 +327,9 @@ function traitdispatch(traittypes, fname) end # check we got a unique trait for dispatch if length(poss)==0 - throw(TraitException("No matching trait found for function $(string(fname))")) + throw(TraitMethodError("No matching trait found for function $(string(fname))")) elseif length(poss)>1 - throw(TraitException("For function $(string(fname)) there are several matching traits:\n $poss")) + throw(TraitMethodError("For function $(string(fname)) there are several matching traits:\n $poss")) end return poss[1] end @@ -378,11 +398,12 @@ function traitfn(fndef) ## 1) Get the existing traits out of the trait_type_f: # These can be retrieved with the call: # trait_type_f(Traits._TraitStorage, ::Type{X}, ::Type{Y}...) for suitable X, Y... - args1 = Any[:(Traits._TraitStorage), get_concrete_type_symb(fn.typs)...] + args1 = Any[:(Traits._TraitStorage), get_concrete_type_symb(fn.typs, length(fn.sig))...] trait_type_f_store_call = makefncall(fn.name, args1) args2 = Any[:(Traits._TraitStorage), fn.typs...] - if has_only_one_method(fn.name, args2) + if has_only_one_method(fn.name, args2, length(fn.sig)) + # TODO/NOTE I don't think eval can be moved to runtime traittypes = eval_curmod(trait_type_f_store_call)[2] else traittypes = Any[] @@ -396,7 +417,7 @@ function traitfn(fndef) ## 3) make new trait-type storage function # tf(::Type{Traits._TraitStorage}, ::Type{X}, ::Type{Y}...) - sig2typs(sig) = [s.args[2] for s in fnt.sig] + sig2typs(sig) = [isa(s,Symbol) ? :Any : s.args[2] for s in fnt.sig] sig = make_Type_sig([:(Traits._TraitStorage), sig2typs(fnt.sig)...]) trait_type_f_store_head = makefnhead(fn.name, fnt.typs, sig) diff --git a/src/traitimpl.jl b/src/traitimpl.jl index d4cb670..b95a95a 100644 --- a/src/traitimpl.jl +++ b/src/traitimpl.jl @@ -120,7 +120,14 @@ end - the type annotations are mandatory. No parameterized methods are allowed (for now). """ -> -macro traitimpl(head, body) +macro traitimpl(args...) + if length(args)==1 + return esc(SimpleTraits.traitimpl(args[1])) + elseif length(args)==2 + head, body = args + else + error("Wrong number of agruments to @traitimpl") + end ## Parse macro header name, paras, trait_expr = parsecurly(head) diff --git a/test/manual-traitdef.jl b/test/manual-traitdef.jl index a6edd34..c9dbcbf 100644 --- a/test/manual-traitdef.jl +++ b/test/manual-traitdef.jl @@ -35,12 +35,12 @@ end @test traitgetsuper(Tr3{A1,A2})==Trait{Tuple{Tr1{A1},Tr2{A1,A2}}} # any type is part of a unconstrained trait: -@test istrait(Tr1{Int}, verbose=verbose) +@test istrait(Tr1{Int}) @test istrait(Tr2{DataType,Int}) @test istrait(Tr3{String,DataType}) @test_throws TraitException istrait(Tr3{:a,7}) # maybe this should error? -@test !istrait(Not{Tr1{Int}}, verbose=verbose) +@test !istrait(Not{Tr1{Int}}) @test !istrait(Not{Tr2{DataType,Int}}) @test !istrait(Not{Tr3{String,DataType}}) @@ -60,7 +60,7 @@ immutable D1{X1} <: Traits.Trait{Tuple{}} end end -@test istrait(D1{Int}, verbose=verbose) +@test istrait(D1{Int}) @test !istrait(D1{String}) immutable D2{X1,X2} <: Traits.Trait{Tuple{D1{X1}, D1{X2}}} diff --git a/test/runtests.jl b/test/runtests.jl index f8c6cb1..c37b6c1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ dispatch_bug1 = true # in traitdispatch.jl # strange Julia issue, see 76ec7fba3a88e # how much output to print -verbose=false +Traits.verbosity(false) # src/Traits.jl tests type A1 end @@ -50,6 +50,8 @@ other_T = f8576.env.defs.tvars @test Traits.find_correponding_type(Tuple{Array{Int,2}, Float64, Tuple{UInt8, UInt16}}, Tuple{Array{I,2}, I, Tuple{UInt8, I}} , I) == Any[Int, Float64, UInt16] +# SimpleTraits +include("simpletraits-compat.jl") # # # manual implementations include("manual-traitdef.jl") diff --git a/test/traitdef.jl b/test/traitdef.jl index 9edbb7b..d3b2754 100644 --- a/test/traitdef.jl +++ b/test/traitdef.jl @@ -90,34 +90,34 @@ index = [Array{Int,2}, StepRange{Int,Int}] c=1 for c in coll # @show IsCollection{c}() # heisenbug protection - @test istrait(IsCollection{c}, verbose=verbose) - @test istrait(IsIterable{c}, verbose=verbose) - @test istrait(IsIterColl{c}, verbose=verbose) + @test istrait(IsCollection{c}) + @test istrait(IsIterable{c}) + @test istrait(IsIterColl{c}) end @test !istrait(IsIndexable{Set}) for c in iter - @test istrait(IsIterable{c}, verbose=verbose) + @test istrait(IsIterable{c}) end for c in dicts - @test istrait(IsAssociative{c}, verbose=verbose) + @test istrait(IsAssociative{c}) end # for c in index - @test istrait(IsIndexable{c}, verbose=verbose) + @test istrait(IsIndexable{c}) end -@test istrait(IsIterable{Array}, verbose=verbose) -@test istrait(IsIterable{ASCIIString}, verbose=verbose) -@test istrait(IsIterable{Int}, verbose=verbose) +@test istrait(IsIterable{Array}) +@test istrait(IsIterable{ASCIIString}) +@test istrait(IsIterable{Int}) @test !istrait(IsIterable{Nothing}) arith = [Int, Float64, Rational{Int}] a1,a2 = 1,1 for a1 in arith for a2 in arith - @test istrait(Arith{a1,a2}, verbose=verbose) + @test istrait(Arith{a1,a2}) end end @@ -459,9 +459,9 @@ end end @test !istrait(TT46{A4758}) @test !istrait(TT46{Dict{Int,Int}}) -# @test istrait(TT46{Set{Int}}, verbose=verbose) this actually works, but not as expected and gives a deprecation warning +# @test istrait(TT46{Set{Int}}) this actually works, but not as expected and gives a deprecation warning @test !istrait(TT46{Int}) -@test istrait(TT46{Array{Int,1}}, verbose=verbose) +@test istrait(TT46{Array{Int,1}}) # TODO: This does not pass currently because of: ## julia> f() = Array{Int}() @@ -470,7 +470,7 @@ end ## julia> Base.return_types(f, ()) ## 1-element Array{Any,1}: ## Array{Int64,0} -# @test istrait(TT46{Array{Int}}, verbose=verbose) -@test istrait(TT46{Array}, verbose=verbose) +# @test istrait(TT46{Array{Int}}) +@test istrait(TT46{Array}) diff --git a/test/traitdispatch.jl b/test/traitdispatch.jl index 1296a96..ddbc9af 100644 --- a/test/traitdispatch.jl +++ b/test/traitdispatch.jl @@ -67,7 +67,7 @@ foobar(a::A, b::A) = a.a==b.a @test ft111(4,5)==6 @test ft111(A(5), A(6))==-999 -@test_throws TraitException ft111("asdf", 5) +@test_throws TraitMethodError ft111("asdf", 5) foobar(a::String, b::Int) = length(a)==b @test ft111("asdf", 4)==-99 @@ -115,11 +115,11 @@ end import Base.sin @traitfn sin{X; MyTr7{X}}(x::X) = bobo(x) @test sin(MTyp71())=="Yeah" -@test_throws TraitException sin(MTyp72()) +@test_throws TraitMethodError sin(MTyp72()) @traitfn Base.cos{X; MyTr7{X}}(x::X) = bobo(x) @test cos(MTyp71())=="Yeah" -@test_throws TraitException cos(MTyp72()) +@test_throws TraitMethodError cos(MTyp72()) ###### # Ambiguities @@ -136,7 +136,7 @@ end end # now ambiguous: -@test_throws Traits.TraitException tf7465(5,6) +@test_throws Traits.TraitMethodError tf7465(5,6) # resolve @traitfn tf7465{X<:Integer,Y; TrTr1{X}, TrTr1{Y}}(x::X,y::Y) = x*y @test tf7465(5,6)==5*6 @@ -146,7 +146,7 @@ end @traitfn tf7465{X<:Integer,Y; TrTr22{X,Y}}(x::X,y::Y) = x*y*1000 # errors again because ambigours again -@test_throws Traits.TraitException tf7465(5,6) +@test_throws Traits.TraitMethodError tf7465(5,6) ## single argument ambiguities #### @@ -166,7 +166,7 @@ end len2(x::Float64) = 55 end -@test_throws Traits.TraitException tttf(5.) # errors +@test_throws Traits.TraitMethodError tttf(5.) # errors @traitfn tttf{X; TrTr1{X}, TrTr2{X}}(x::X) = len2(x) # pick len2 over len1 @test tttf(5.)==len2(5.) @@ -214,7 +214,7 @@ end # This test in traitdispatch.jl should probably pass: if dispatch_bug1 - @test_throws Traits.TraitException tttf238(5)=="this should win" + @test_throws Traits.TraitMethodError tttf238(5)=="this should win" else @test tttf238(5)=="this should win" end @@ -256,4 +256,4 @@ end @traitfn tttf240{X; VV1{X}}(x::X) = "neither should win 1" @traitfn tttf240{X; VV2{X}}(x::X) = "neither should win 2" -@test_throws Traits.TraitException tttf240(5) +@test_throws Traits.TraitMethodError tttf240(5) diff --git a/test/traitfns.jl b/test/traitfns.jl index e7bbb3a..8f9c809 100644 --- a/test/traitfns.jl +++ b/test/traitfns.jl @@ -51,7 +51,7 @@ b.body =:() @test Traits.makefncall(f1e_p.name, f1e_p.sig)==:(f1(x,y)) -@test Traits.get_concrete_type_symb(f1e_p.typs)==Any[:Int, :Any] +@test Traits.get_concrete_type_symb(f1e_p.typs, 2)==Any[:Int, :Any] @test Traits.make_Type_sig([s.args[2] for s in f1e_p.sig])==Any[:(::Type{X}), :(::Type{TT})] @@ -290,5 +290,5 @@ fn788(b::Int, c::Int) = b @traitfn ff876{T; Pr333{T}, !Pr334{T} }(x::T) = 2x @traitfn ff876{T; !Pr333{T}, !Pr334{T} }(x::T) = 2000x -@test_throws TraitException ff876(5) +@test_throws TraitMethodError ff876(5) @test ff876(5.0)==2000*5.0 From c960809d41af68e4114d02e330da605a320dbb95 Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Sun, 28 Jun 2015 21:39:38 -0400 Subject: [PATCH 7/7] TODO: fix errors in traitfns.jl test, remove show Hopefully that will be it... --- src/traitfns.jl | 59 +++++++++++++++++++++++++++++++----------------- test/traitfns.jl | 30 +++++++++++++++--------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/traitfns.jl b/src/traitfns.jl index a723d42..9773693 100644 --- a/src/traitfns.jl +++ b/src/traitfns.jl @@ -22,12 +22,16 @@ Base.done(::GenerateTypeVars, state) = false type ParsedFn # (probably should adapt MetaTools.jl...) name::FName # f1 fun::Expr # :(f1{X<:Int,Y}) - typs::Vector{Any} # [:(X<:Int),:Y] + typs::Vector{Any} # these are the type(var) which are method parameters [:(X<:Int),:Y] + typs2::Vector{Any} # these are the type(var) sig::Vector{Any} # [:(x::X), :(y::Y)] traits::Vector{Any} # [D1{X}, D2{X,Y}] body::Expr # quote ... end end +# translates a signature to types +sig2typs(sig) = [isa(s,Symbol) ? :Any : s.args[2] for s in sig] + # Parsing: addit!(traits, t) = push!(traits, SimpleTraits.isnegated(t) ? :(Not{$(t.args[2])}) : t) function istraittuple(t::Expr) @@ -50,6 +54,7 @@ function parsetraitfn_head(head::Expr) name = nametyp.args[1] fun = Expr(:curly, deepcopy(nametyp.args[[1;3:end]])...) typs = fun.args[2:end] + typs2 = sig2typs(sig) traits = Any[] for t in nametyp.args[2].args if istraittuple(t) @@ -60,7 +65,7 @@ function parsetraitfn_head(head::Expr) addit!(traits, t) end end - return ParsedFn(name, fun, typs, sig, traits, :()) + return ParsedFn(name, fun, typs, typs2, sig, traits, :()) end gettypesymbol(x::Expr) = x.args[1] # :(X1<:Int) @@ -191,34 +196,42 @@ function makefncall(fname, sig) # (i.e. strips the extraneous type-assertions) return :( $fname( $(strip_typeasserts(sig)...) ) ) end -function get_concrete_type_symb(typs, len) # surely there are - # never more that 10^5 - # arguments - +function get_concrete_type_symb(typs, typs2, len) # Returns the most general type satisfying typs as a list of symbols: # [:(X<:Int), :Y] -> [:Int, :Any] + # + # if len>length(typs) then fill with any. + @show typs, typs2, len out = Any[] + trans = Dict() for t in typs - if isa(t, Symbol) - push!(out, :Any) - elseif t.head==:. - push!(out, t) + @show t + if isa(t, Symbol) + trans[t] = :Any else - push!(out, t.args[2]) + @show t.args[1], t.args[2] + trans[t.args[1]] = t.args[2] end end + @show trans + for t in typs2 + push!(out, get(trans, t, t)) + end + @show out # pad with Any for i=1:(len-length(out)) push!(out, :Any) end + @show out return out end -function get_concrete_type_Typetuple(typs, len) +function get_concrete_type_Typetuple(typs, typs2, len) # Returns the most general type satisfying typs as a tuple of # actual types: # [:(X<:Int), :Y] -> Tuple{Int, Any} - out = get_concrete_type_symb(typs, len) + out = get_concrete_type_symb(typs, typs2, len) for i in 1:length(out) + # TODO: can this eval be removed? out[i] = Type{eval_curmod(out[i])} end for i=1:(len-length(out)) @@ -244,22 +257,22 @@ function make_Type_sig(typs) end return out end -function has_only_one_method(fname::Symbol, typs, len) +function has_only_one_method(fname::Symbol, typs, typs2, len) # Checks whether fname has one and only one method for types in # typs. if isdefined(current_module(), fname) 1 == length( methods(eval_curmod(fname), - get_concrete_type_Typetuple(typs, len)) ) + get_concrete_type_Typetuple(typs, typs2, len)) ) else false end end -function has_only_one_method(fname::Expr, typs, len) +function has_only_one_method(fname::Expr, typs, typs2, len) # Checks whether fname has one and only one method for types in # typs. if isdefined(eval_curmod(fname.args[1]), fname.args[2].args[1]) 1 == length( methods(eval_curmod(fname), - get_concrete_type_Typetuple(typs, len)) ) + get_concrete_type_Typetuple(typs, typs2, len)) ) else false end @@ -398,11 +411,16 @@ function traitfn(fndef) ## 1) Get the existing traits out of the trait_type_f: # These can be retrieved with the call: # trait_type_f(Traits._TraitStorage, ::Type{X}, ::Type{Y}...) for suitable X, Y... - args1 = Any[:(Traits._TraitStorage), get_concrete_type_symb(fn.typs, length(fn.sig))...] - trait_type_f_store_call = makefncall(fn.name, args1) + @show fn.sig + @show fn.typs + @show fn.typs2 + +@show args1 = Any[:(Traits._TraitStorage), get_concrete_type_symb(fn.typs, fn.typs2, length(fn.sig))...] +@show trait_type_f_store_call = makefncall(fn.name, args1) args2 = Any[:(Traits._TraitStorage), fn.typs...] - if has_only_one_method(fn.name, args2, length(fn.sig)) +@show args2_ = Any[:(Traits._TraitStorage), fn.typs2...] + if has_only_one_method(fn.name, args2, args2_, length(fn.sig)) # TODO/NOTE I don't think eval can be moved to runtime traittypes = eval_curmod(trait_type_f_store_call)[2] else @@ -417,7 +435,6 @@ function traitfn(fndef) ## 3) make new trait-type storage function # tf(::Type{Traits._TraitStorage}, ::Type{X}, ::Type{Y}...) - sig2typs(sig) = [isa(s,Symbol) ? :Any : s.args[2] for s in fnt.sig] sig = make_Type_sig([:(Traits._TraitStorage), sig2typs(fnt.sig)...]) trait_type_f_store_head = makefnhead(fn.name, fnt.typs, sig) diff --git a/test/traitfns.jl b/test/traitfns.jl index 8f9c809..1a861ed 100644 --- a/test/traitfns.jl +++ b/test/traitfns.jl @@ -10,7 +10,8 @@ f1e_function = :(function f1{X<:Int,TT; D1{X}, D2{X,TT}}(x::X,y::TT) f1e_p = Traits.ParsedFn( :f1, :(f1{X<:Int,TT}), - Any[Expr(:<:, :X, :Int),:TT], + Any[Expr(:<:, :X, :Int),:TT], + Any[:X,:TT], Any[:(x::X), :(y::TT)], Any[:(D1{X}), :(D2{X,TT})], :(())) @@ -18,6 +19,7 @@ f1e_pt = Traits.ParsedFn( :f1, :(f1{X1<:Int,X2}), Any[Expr(:<:, :X1, :Int),:X2], + Any[:X1,:X2], Any[:(x1::X1), :(x2::X2)], Any[:(D1{X1}), :(D2{X1,X2})], :(())) @@ -61,6 +63,7 @@ f1e2_pt = Traits.ParsedFn( :f1, :(f1{X1<:Int}), Any[Expr(:<:, :X1, :Int)], + Any[:X, :X], Any[:(x1::X1), :(x2::X1)], Any[:(D1{X1}), :(D2{X1,X1})], :(())) @@ -71,7 +74,8 @@ f1ee = :(tfd{K, V; D2{K,V}}(x::Vector{K}, y::V) = ()) f1ee_p = Traits.ParsedFn( :tfd, :(tfd{K,V}), - Any[:K,:V], + Any[:K,:V], + Any[:Vector{K},:V], Any[:(x::Vector{K}), :(y::V)], Any[:(D2{K,V})], :(())) @@ -205,22 +209,13 @@ end end import Mod1.tf1 -# @show tf1(Traits._TraitStorage, MTyp1, Int) -# @show tf1(Traits._TraitStorage,Any,Int) -# typs = Any[:(Traits._TraitStorage),:X, Expr(:<:, :X1, :Int)] -# methods(eval(:tf1), Traits. get_concrete_type_Typetuple(typs)) println(" These warnings are ok:") # Well, I'm not sure whether they are ok. But at least normal... @traitfn tf1{X, Y<:Int; M1Tr100{X}}(a::X, b::Y) = foofoo(a)^b println(" endof ok-warnings.") -#methods(eval(:tf1), Traits. get_concrete_type_Typetuple(typs)) -#@show tf1(Traits._TraitStorage, Any, Int) @test tf1(MTyp1(7), 5)=="MTyp1, barbar"^5 @test tf1(MTyp00(9), 5)== "MTyp00 foofoo"^5 -# @show tf1(Traits._TraitStorage, MTyp1, Int) -# @show tf1(Traits._TraitStorage, Any, Int) - # # error: @test tf1(MTyp99(9), 5)=="MTyp99, barbar"^5 @@ -292,3 +287,16 @@ fn788(b::Int, c::Int) = b @traitfn ff876{T; !Pr333{T}, !Pr334{T} }(x::T) = 2000x @test_throws TraitMethodError ff876(5) @test ff876(5.0)==2000*5.0 + +### +# un-parameterized args +### +@traitfn ff877{T; Pr333{T}, !Pr334{T} }(x::T, y) = 2x +@traitfn ff877{T; !Pr333{T}, !Pr334{T} }(x::T, y) = "a" +@traitfn ff877{T; !Pr333{T}, !Pr334{T} }(x::T, y::Int) = -x +@traitfn ff877{T; !Pr333{T}, !Pr334{T} }(x::T, y::Int, z) = -2x +@test_throws TraitMethodError ff877(5, 6) +@test ff877(5.0, "a")=="a" +@test ff877(5.0, 5)==-5.0 +@test ff877(5.0, 5, "a")==-10.0 +