Skip to content

Commit

Permalink
Merge pull request #4 from mbauman/mb/rename
Browse files Browse the repository at this point in the history
Use names from base (without extending them)
  • Loading branch information
mbauman committed Jun 20, 2015
2 parents b4c2d65 + b6a4186 commit 2bb690a
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 115 deletions.
81 changes: 14 additions & 67 deletions src/TupleTypes.jl
Original file line number Diff line number Diff line change
@@ -1,67 +1,14 @@
module TupleTypes

import Base: isvatuple

check(T) = (T===Tuple || T===NTuple) && throw(ArgumentError("parameters of $T are undefined"))

# Define methods for up to N tuple parameters to be evaluated in the Type domain
const N=4
ie = :()
for p=0:N
params = ntuple(i->symbol(:P,i), p)
global getpara, concatenate
# Return the whole parameter list as an svec
@eval getpara{$(params...)}(::Type{Tuple{$(params...)}}) = Base.svec($(params...))

# Accessing by a constant value
for i=1:p
@eval getpara{$(params...)}(::Type{Tuple{$(params...)}}, ::Type{Val{$i}}) = $(params[i])
end
@eval getpara{$(params...), n}(t::Type{Tuple{$(params...)}}, ::Type{Val{n}}) = throw(BoundsError(t, n))

# Accessing by a non-constant selects the proper value with an ifelse chain
if p == 1
ie = :(P1)
elseif p > 1
ie = :(ifelse(i == $p, $(params[p]), $ie))
end
@eval getpara{$(params...)}(t::Type{Tuple{$(params...)}}, i::Int) = (1 <= i <= $p || throw(BoundsError(t, i)); $ie)
# It'd be nice to simply define the constant `i` computation in the type
# domain instead of relying upon dispatch, but it's not constant-folding:
# @eval getparam{$(params...), i}(t::Type{Tuple{$(params...)}}, ::Type{Val{i}}) = (1 <= i <= $p || throw(BoundsError(t, i)); $ie)

# concatenation
for q=0:N
qarams = ntuple(i->symbol(:Q,i), q)
@eval concatenate{$(params...), $(qarams...)}(::Type{Tuple{$(params...)}},::Type{Tuple{$(qarams...)}}) = Tuple{$(params...), $(qarams...)}
end
end


# Might as well use this for all datatypes
function getpara(T::Type)
check(T)
T.parameters # just return the svec, avoids allocations
end

function getpara(T::Type, i::Integer)
check(T)
L = length(T.parameters)
if isvatuple(T) && i >= L
T.parameters[end].parameters[1]
else
1 <= i <= L || throw(BoundsError(T, i))
T.parameters[i]
end
end

getpara{I<:Integer}(T::Type, is::AbstractVector{I}) = Base.svec([getpara(T,i) for i in is]...)

function concatenate{T<:Tuple, S<:Tuple}(::Type{T}, ::Type{S})
check(T); check(S);
isvatuple(T) && throw(ArgumentError("cannot concatenate the varargs tuple $T with $S"))
Tuple{T.parameters..., S.parameters...}
end


end # module
baremodule TupleTypes
using Base

# Since we're defining our own getindex and length methods to operate on types
# separately, it's difficult to then use Base's methods at the same time.
# Instead, we define them as t* within an Implementation module, and reassign
# them to the API names we want to use in this baremodule.
Base.include("implementation.jl")
const getindex = Implementation.tgetindex
const length = Implementation.tlength
const collect = Implementation.tcollect
const concatenate = Implementation.concatenate

end
74 changes: 74 additions & 0 deletions src/implementation.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
module Implementation

import Base: isvatuple

check(T) = (T===Tuple || T===NTuple) && throw(ArgumentError("parameters of $T are undefined"))

# Define methods for up to N tuple parameters to be evaluated in the Type domain
const N=4
ie = :()
for p=0:N
params = ntuple(i->symbol(:P,i), p)
global tgetindex, tlength, concatenate

# Accessing by a constant value
for i=1:p
@eval tgetindex{$(params...)}(::Type{Tuple{$(params...)}}, ::Type{Val{$i}}) = $(params[i])
end
@eval tgetindex{$(params...), n}(t::Type{Tuple{$(params...)}}, ::Type{Val{n}}) = throw(BoundsError(t, n))

# Accessing by a non-constant selects the proper value with an ifelse chain
if p == 1
ie = :(P1)
elseif p > 1
ie = :(ifelse(i == $p, $(params[p]), $ie))
end
@eval tgetindex{$(params...)}(t::Type{Tuple{$(params...)}}, i::Int) = (1 <= i <= $p || throw(BoundsError(t, i)); $ie)
# It'd be nice to simply define the constant `i` computation in the type
# domain instead of relying upon dispatch, but it's not constant-folding:
# @eval getparam{$(params...), i}(t::Type{Tuple{$(params...)}}, ::Type{Val{i}}) = (1 <= i <= $p || throw(BoundsError(t, i)); $ie)

# concatenation
for q=0:N
qarams = ntuple(i->symbol(:Q,i), q)
@eval concatenate{$(params...), $(qarams...)}(::Type{Tuple{$(params...)}},::Type{Tuple{$(qarams...)}}) = Tuple{$(params...), $(qarams...)}
end

# length
@eval tlength{$(params...)}(t::Type{Tuple{$(params...)}}) = $p
end


# Fallbacks for the general case
function tcollect(T::Type)
check(T)
T.parameters # just return the svec, avoids allocations
end

function tlength(T::Type)
check(T)
length(T.parameters)
end

tgetindex{i}(T::Type, ::Type{Val{i}}) = tgetindex(T, i)
function tgetindex(T::Type, i::Integer)
check(T)
L = length(T.parameters)
if isvatuple(T) && i >= L
T.parameters[end].parameters[1]
else
1 <= i <= L || throw(BoundsError(T, i))
T.parameters[i]
end
end

tgetindex{I<:Integer}(T::Type, is::AbstractVector{I}) = Base.svec([tgetindex(T,i) for i in is]...)

function concatenate{T<:Tuple, S<:Tuple}(::Type{T}, ::Type{S})
check(T); check(S);
isvatuple(T) && throw(ArgumentError("cannot concatenate the varargs tuple $T with $S"))
Tuple{T.parameters..., S.parameters...}
end


end # module
122 changes: 74 additions & 48 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,61 +1,87 @@
using TupleTypes
using Base.Test

import TupleTypes: getpara, concatenate

## getpara

@test getpara(Tuple{}) === Core.svec()
@test getpara(Tuple{1,2,3}) === Core.svec(1,2,3)
@test getpara(Tuple{Int,String}) === Core.svec(Int,String)
@test_throws ArgumentError getpara(Tuple)
@test_throws ArgumentError getpara(NTuple)
#@test_throws ArgumentError getpara(Tuple{Vararg{Int}})
#@test_throws ArgumentError getpara(Tuple{String,Vararg{Int}})

## getpara
@test getpara(Tuple{1,2,3}, 1) === 1
@test getpara(Tuple{1,2,3}, 2) === 2
@test getpara(Tuple{1,2,3}, 3) === 3
@test getpara(Tuple{1,2,3}, 2:3) === Base.svec(2,3)
@test_throws BoundsError getpara(Tuple{1,2,3}, 0)
@test_throws BoundsError getpara(Tuple{1,2,3}, 4)
@test getpara(Tuple{Int, String}, 1) === Int
@test getpara(Tuple{Int, String}, 2) === String
@test getpara(Tuple{Int, String}, [2,1]) === Base.svec(String,Int)
@test_throws BoundsError getpara(Tuple{Int, String}, 0)
@test_throws BoundsError getpara(Tuple{Int, String}, 3)
@test_throws ArgumentError getpara(Tuple, 1)
@test_throws ArgumentError getpara(NTuple, 1)
@test getpara(Tuple{Vararg{Int}}, 1) === Int
@test getpara(Tuple{Vararg{Int}}, 1000) === Int
@test getpara(Tuple{Vararg{Int}}, 10^10) === Int
@test getpara(Tuple{Vararg{Int}}, [10^10, 10^10+1]) === Base.svec(Int,Int)
@test_throws BoundsError getpara(Tuple{Vararg{Int}}, 0)
@test getpara(Tuple{Int, Vararg{String}}, 1) === Int
@test getpara(Tuple{Int, Vararg{String}}, 2) === String
@test getpara(Tuple{Int, Vararg{String}}, 3) === String
@test getpara(Tuple{Int, Vararg{String}}, [1,3]) === Base.svec(Int,String)
@test_throws BoundsError getpara(Tuple{Int, Vararg{String}}, 0)
## TupleTypes.collect

@test TupleTypes.collect(Tuple{}) === Core.svec()
@test TupleTypes.collect(Tuple{1,2,3}) === Core.svec(1,2,3)
@test TupleTypes.collect(Tuple{Int,String}) === Core.svec(Int,String)
@test_throws ArgumentError TupleTypes.collect(Tuple)
@test_throws ArgumentError TupleTypes.collect(NTuple)
#@test_throws ArgumentError TupleTypes.collect(Tuple{Vararg{Int}})
#@test_throws ArgumentError TupleTypes.collect(Tuple{String,Vararg{Int}})

## TupleTypes.getindex
@test TupleTypes.getindex(Tuple{1,2,3}, 1) === 1
@test TupleTypes.getindex(Tuple{1,2,3}, 2) === 2
@test TupleTypes.getindex(Tuple{1,2,3}, 3) === 3
@test TupleTypes.getindex(Tuple{1,2,3}, Val{1}) === 1
@test TupleTypes.getindex(Tuple{1,2,3}, Val{2}) === 2
@test TupleTypes.getindex(Tuple{1,2,3}, Val{3}) === 3
@test TupleTypes.getindex(Tuple{1,2,3}, 2:3) === Base.svec(2,3)
@test_throws BoundsError TupleTypes.getindex(Tuple{1,2,3}, 0)
@test_throws BoundsError TupleTypes.getindex(Tuple{1,2,3}, 4)
@test_throws BoundsError TupleTypes.getindex(Tuple{1,2,3}, Val{0})
@test_throws BoundsError TupleTypes.getindex(Tuple{1,2,3}, Val{4})
@test TupleTypes.getindex(Tuple{Int, String}, 1) === Int
@test TupleTypes.getindex(Tuple{Int, String}, 2) === String
@test TupleTypes.getindex(Tuple{Int, String}, Val{1}) === Int
@test TupleTypes.getindex(Tuple{Int, String}, Val{2}) === String
@test TupleTypes.getindex(Tuple{Int, String}, [2,1]) === Base.svec(String,Int)
@test_throws BoundsError TupleTypes.getindex(Tuple{Int, String}, 0)
@test_throws BoundsError TupleTypes.getindex(Tuple{Int, String}, 3)
@test_throws BoundsError TupleTypes.getindex(Tuple{Int, String}, Val{0})
@test_throws BoundsError TupleTypes.getindex(Tuple{Int, String}, Val{3})
@test_throws ArgumentError TupleTypes.getindex(Tuple, 1)
@test_throws ArgumentError TupleTypes.getindex(NTuple, 1)
@test TupleTypes.getindex(Tuple{Vararg{Int}}, Val{1}) === Int
@test TupleTypes.getindex(Tuple{Vararg{Int}}, Val{1000}) === Int
@test TupleTypes.getindex(Tuple{Vararg{Int}}, Val{10^10}) === Int
@test TupleTypes.getindex(Tuple{Vararg{Int}}, [10^10, 10^10+1]) === Base.svec(Int,Int)
@test_throws BoundsError TupleTypes.getindex(Tuple{Vararg{Int}}, 0)
@test_throws BoundsError TupleTypes.getindex(Tuple{Vararg{Int}}, Val{0})
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, 1) === Int
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, 2) === String
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, 3) === String
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, Val{1}) === Int
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, Val{2}) === String
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, Val{3}) === String
@test TupleTypes.getindex(Tuple{Int, Vararg{String}}, [1,3]) === Base.svec(Int,String)
@test_throws BoundsError TupleTypes.getindex(Tuple{Int, Vararg{String}}, 0)
@test_throws BoundsError TupleTypes.getindex(Tuple{Int, Vararg{String}}, Val{0})

# bug in Julia https://github.com/JuliaLang/julia/issues/11725
f{T<:Integer}(::T, ::T) = 1
fm = first(methods(f))
g{I<:Integer}(::I, ::I) = 1
gm = first(methods(g))
@test getpara(fm.sig, 1)==fm.sig.parameters[1]
@test getpara(gm.sig, 1)==gm.sig.parameters[1]
@test TupleTypes.getindex(fm.sig, 1)==fm.sig.parameters[1]
@test TupleTypes.getindex(gm.sig, 1)==gm.sig.parameters[1]


## Concatenate

@test concatenate(Tuple{}, Tuple{}) === Tuple{}
@test concatenate(Tuple{Int}, Tuple{}) === Tuple{Int}
@test concatenate(Tuple{}, Tuple{Int}) === Tuple{Int}
@test concatenate(Tuple{Int}, Tuple{String}) === Tuple{Int,String}
@test concatenate(Tuple{1,2,3}, Tuple{4,5,6}) === Tuple{1,2,3,4,5,6}
@test concatenate(Tuple{}, Tuple{Vararg{Int}}) === Tuple{Vararg{Int}}
@test concatenate(Tuple{String}, Tuple{Int, Vararg{String}}) === Tuple{String, Int, Vararg{String}}
@test_throws ArgumentError concatenate(Tuple, Tuple{Int})
@test_throws ArgumentError concatenate(Tuple{}, Tuple)
@test_throws ArgumentError concatenate(Tuple{Vararg{String}}, Tuple{Int})
@test TupleTypes.concatenate(Tuple{}, Tuple{}) === Tuple{}
@test TupleTypes.concatenate(Tuple{Int}, Tuple{}) === Tuple{Int}
@test TupleTypes.concatenate(Tuple{}, Tuple{Int}) === Tuple{Int}
@test TupleTypes.concatenate(Tuple{Int}, Tuple{String}) === Tuple{Int,String}
@test TupleTypes.concatenate(Tuple{1,2,3}, Tuple{4,5,6}) === Tuple{1,2,3,4,5,6}
@test TupleTypes.concatenate(Tuple{}, Tuple{Vararg{Int}}) === Tuple{Vararg{Int}}
@test TupleTypes.concatenate(Tuple{String}, Tuple{Int, Vararg{String}}) === Tuple{String, Int, Vararg{String}}
@test_throws ArgumentError TupleTypes.concatenate(Tuple, Tuple{Int})
@test_throws ArgumentError TupleTypes.concatenate(Tuple{}, Tuple)
@test_throws ArgumentError TupleTypes.concatenate(Tuple{Vararg{String}}, Tuple{Int})


## Length

@test TupleTypes.length(Tuple{}) === 0
@test TupleTypes.length(Tuple{Int}) === 1
@test TupleTypes.length(Tuple{Int8, Int16}) === 2
@test TupleTypes.length(Tuple{Int8, Int16, Int32}) === 3
@test TupleTypes.length(Tuple{Int8, Int16, Int32, Int64}) === 4
@test TupleTypes.length(Tuple{Vararg{String}}) === 1
@test TupleTypes.length(Tuple{Int,Vararg{String}}) === 2
@test TupleTypes.length(Tuple{Int8, Int16,Vararg{String}}) === 3
@test TupleTypes.length(Tuple{Int8, Int16, Int32,Vararg{String}}) === 4
@test TupleTypes.length(Tuple{Int8, Int16, Int32, Int64,Vararg{String}}) === 5

0 comments on commit 2bb690a

Please sign in to comment.