Skip to content

Commit

Permalink
Merge #5
Browse files Browse the repository at this point in the history
5: Add DisptachedTupleSet, bump version r=charleskawczynski a=charleskawczynski

This PR adds `DispatchedTupleSet`, and subtypes a newly added `AbstractDispatchedTuple`. `DispatchedTupleSet`'s require unique keys and return values instead of tuples of values.

Co-authored-by: Charles Kawczynski <[email protected]>
  • Loading branch information
bors[bot] and charleskawczynski authored Mar 2, 2021
2 parents 69988d8 + d621c0f commit cb3aa0c
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DispatchedTuples"
uuid = "508c55e1-51b4-41fd-a5ca-7eb0327d070d"
authors = ["Charles Kawczynski <[email protected]>"]
version = "0.1.0"
version = "0.1.1"

[compat]
julia = "1.5"
2 changes: 2 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ For convenience, `DispatchedTuple` can alternatively take a `Tuple` of two-eleme
## API

```@docs
DispatchedTuples.AbstractDispatchedTuple
DispatchedTuples.DispatchedTuple
DispatchedTuples.DispatchedTupleSet
DispatchedTuples.dispatch
```
82 changes: 78 additions & 4 deletions src/DispatchedTuples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ module DispatchedTuples

import Base

export DispatchedTuple, dispatch
export AbstractDispatchedTuple, DispatchedTuple, DispatchedTupleSet, dispatch

struct NoDefaults end

"""
AbstractDispatchedTuple{T <: Tuple, D}
An abstract dispatch tuple type, for sub-typing
dispatched tuples.
"""
abstract type AbstractDispatchedTuple{T <: Tuple, D} end

"""
DispatchedTuple(tup[, default_value])
Expand All @@ -28,7 +36,7 @@ with a key it hasn't seen, an error is thrown.
For convenience, `DispatchedTuple` can alternatively take a
`Tuple` of two-element `Tuple`s.
"""
struct DispatchedTuple{T,D}
struct DispatchedTuple{T,D} <: AbstractDispatchedTuple{T, D}
tup::T
default::D
function DispatchedTuple(tup_in::T, default=NoDefaults()) where {T<:Tuple}
Expand Down Expand Up @@ -80,7 +88,73 @@ end
expr
end

Base.isempty(dt::DispatchedTuple) = Base.isempty(dt.tup)
Base.length(dt::DispatchedTuple) = Base.length(dt.tup)
Base.isempty(dt::AbstractDispatchedTuple) = Base.isempty(dt.tup)
Base.length(dt::AbstractDispatchedTuple) = Base.length(dt.tup)

"""
DispatchedTupleSet(tup[, default_value])
Similar to `DispatchedTuple`, except:
- keys must be unique.
- returns the value, and not a tuple of values.
- throws an error in `dispatch` if keys are not unique.
"""
struct DispatchedTupleSet{T,D} <: AbstractDispatchedTuple{T, D}
tup::T
default::D
function DispatchedTupleSet(tup_in::T, default=NoDefaults()) where {T<:Tuple}
if eltype(tup_in) <: Pair
tup = map(x->(x.first, x.second), tup_in)
else
tup = tup_in
end
return new{typeof(tup), typeof(default)}(tup, default)
end
end

"""
dispatch(::DispatchedTupleSet, type_instance)
Dispatch on the [`DispatchedTupleSet`](@ref), based
on the instance of the input type `type_instance`.
"""
@generated function dispatch(dt::DispatchedTupleSet{TT, NoDefaults}, ::T) where {TT, T}
match_count = 0
expr = quote end
for (i,k) in enumerate(fieldnames(TT))
if first_eltype(fieldtype(TT, i)) == T
match_count += 1
expr = :(dt.tup[$i][2])
end
end
if match_count == 0
return :(throw(error("No method dispatch defined for type $T")))
elseif match_count > 1
return :(throw(error("DispatchedTupleSet has non-unique keys for type $T")))
else
return expr
end
end

@generated function dispatch(dt::DispatchedTupleSet{TT,D}, ::T) where {TT, D, T}
match_count = 0
expr = quote end
for (i,k) in enumerate(fieldnames(TT))
if first_eltype(fieldtype(TT, i)) == T
match_count += 1
expr = :(dt.tup[$i][2])
end
end
if match_count == 0
return :(dt.default)
elseif match_count > 1
return :(throw(error("DispatchedTupleSet has non-unique keys for type $T")))
else
return expr
end
end

# Nested dispatch calls:
dispatch(dt::AbstractDispatchedTuple, a, b...) = dispatch(dispatch(dt, a), b...)

end # module
102 changes: 71 additions & 31 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,39 @@ struct Foo end
struct Bar end
struct FooBar end

tup = (
(Foo(), 1),
(Bar(), 2),
)
#####
##### DispatchedTuple's
#####

@testset "DispatchedTuples - error by default" begin
dt = DispatchedTuple(tup)
@testset "DispatchedTuples - base behavior" begin
dt = DispatchedTuple(((Foo(), 1), (Bar(), 2)))
@test dispatch(dt, Foo()) == (1,)
@test dispatch(dt, Bar()) == (2,)
@test_throws ErrorException dispatch(dt, FooBar())
end

@testset "DispatchedTuples - has default behavior" begin
dt = DispatchedTuple(tup, 0)
dt = DispatchedTuple(((Foo(), 1), (Bar(), 2)), 0)
@test dispatch(dt, Foo()) == (1,)
@test dispatch(dt, Bar()) == (2,)
@test dispatch(dt, FooBar()) == (dt.default,)
end

tup = (
(Foo(), 1),
(Bar(), 2),
(Foo(), 3),
)

@testset "DispatchedTuples - multiple values" begin
dt = DispatchedTuple(tup)
@test dispatch(dt, Foo()) == (1,3)
@testset "DispatchedTuples - base behavior - Pair interface" begin
dt = DispatchedTuple((Pair(Foo(), 1), Pair(Bar(), 2)))
@test dispatch(dt, Foo()) == (1,)
@test dispatch(dt, Bar()) == (2,)
@test_throws ErrorException dispatch(dt, FooBar())
end

tup = (
Pair(Foo(), 1),
Pair(Bar(), 2),
)

@testset "DispatchedTuples - Pair interface - error by default" begin
dt = DispatchedTuple(tup)
dt = DispatchedTuple((Pair(Foo(), 1), Pair(Bar(), 2)), 0)
@test dispatch(dt, Foo()) == (1,)
@test dispatch(dt, Bar()) == (2,)
@test_throws ErrorException dispatch(dt, FooBar())
@test dispatch(dt, FooBar()) == (dt.default,)
end

@testset "DispatchedTuples - Pair interface - has default behavior" begin
dt = DispatchedTuple(tup, 0)
@test dispatch(dt, Foo()) == (1,)
@testset "DispatchedTuples - multiple values" begin
dt = DispatchedTuple(((Foo(), 1), (Bar(), 2), (Foo(), 3)))
@test dispatch(dt, Foo()) == (1,3)
@test dispatch(dt, Bar()) == (2,)
@test dispatch(dt, FooBar()) == (dt.default,)
@test_throws ErrorException dispatch(dt, FooBar())
end

@testset "DispatchedTuples - extending" begin
Expand All @@ -66,3 +50,59 @@ end
@test length(dt) == length(tup)
@test isempty(dt) == isempty(tup)
end

#####
##### DispatchedTupleSet's
#####

@testset "DispatchedTupleSet - base behavior" begin
dt = DispatchedTupleSet(((Foo(), 1), (Bar(), 2)))
@test dispatch(dt, Foo()) == 1
@test dispatch(dt, Bar()) == 2
@test_throws ErrorException dispatch(dt, FooBar())

dt = DispatchedTupleSet(((Foo(), 1), (Bar(), 2)), 0)
@test dispatch(dt, Foo()) == 1
@test dispatch(dt, Bar()) == 2
@test dispatch(dt, FooBar()) == dt.default
end

@testset "DispatchedTupleSet - base behavior - Pair interface" begin
dt = DispatchedTupleSet((Pair(Foo(), 1), Pair(Bar(), 2)))
@test dispatch(dt, Foo()) == 1
@test dispatch(dt, Bar()) == 2
@test_throws ErrorException dispatch(dt, FooBar())
end

@testset "DispatchedTupleSet - multiple values, unique keys" begin
dt = DispatchedTupleSet(((Foo(), 1), (Bar(), 2), (Foo(), 3)))
@test_throws ErrorException dispatch(dt, Foo()) == 1
@test dispatch(dt, Bar()) == 2
@test_throws ErrorException dispatch(dt, FooBar())

dt = DispatchedTupleSet(((Foo(), 1), (Bar(), 2), (Foo(), 3)), 0)
@test_throws ErrorException dispatch(dt, Foo()) == 1
@test dispatch(dt, Bar()) == 2
@test dispatch(dt, FooBar()) == dt.default
end

@testset "DispatchedTuples - nested" begin
dtup_L1_a = DispatchedTupleSet(((Foo(), 1), (Bar(), 2)))
dtup_L1_b = DispatchedTupleSet(((Foo(), 3), (Bar(), 4)))
dtup_L1_c = DispatchedTupleSet(((Foo(), 5), (Bar(), 6)))
dtup_L1_d = DispatchedTupleSet(((Foo(), 7), (Bar(), 8)))

dtup_L2_a = DispatchedTupleSet(((Foo(), dtup_L1_a), (Bar(), dtup_L1_b)))
dtup_L2_b = DispatchedTupleSet(((Foo(), dtup_L1_c), (Bar(), dtup_L1_d)))

dtup_L3_a = DispatchedTupleSet(((Foo(), dtup_L2_a), (Bar(), dtup_L2_b)))

@test dispatch(dtup_L3_a, Foo(), Foo(), Foo()) == 1
@test dispatch(dtup_L3_a, Foo(), Foo(), Bar()) == 2
@test dispatch(dtup_L3_a, Foo(), Bar(), Foo()) == 3
@test dispatch(dtup_L3_a, Foo(), Bar(), Bar()) == 4
@test dispatch(dtup_L3_a, Bar(), Foo(), Foo()) == 5
@test dispatch(dtup_L3_a, Bar(), Foo(), Bar()) == 6
@test dispatch(dtup_L3_a, Bar(), Bar(), Foo()) == 7
@test dispatch(dtup_L3_a, Bar(), Bar(), Bar()) == 8
end

2 comments on commit cb3aa0c

@charleskawczynski
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/31171

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.1.1 -m "<description of version>" cb3aa0c8a5f8b4a0b59dbf91ca663ef5de88d1f0
git push origin v0.1.1

Please sign in to comment.