Skip to content

Commit

Permalink
Merge #7
Browse files Browse the repository at this point in the history
7: Add DispatchedTupleDict, improve readme r=charleskawczynski a=charleskawczynski

This PR adds `DispatchedTupleDict`s, which allow multiple keys. Also, the README description is a bit improved. 

Co-authored-by: Charles Kawczynski <[email protected]>
  • Loading branch information
bors[bot] and charleskawczynski authored Mar 6, 2021
2 parents 89a26e7 + edec7e8 commit 5ab1763
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 11 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.2"
version = "0.1.3"

[compat]
julia = "1.5"
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,23 @@
[bors-img]: https://bors.tech/images/badge_small.svg
[bors-url]: https://app.bors.tech/repositories/32073

DispatchedTuples.jl defines one user-facing type: `DispatchedTuple`, and one user-facing method: `dispatch`. A `DispatchedTuple` is similar to a compile-time dictionary, that uses dispatch for the look-up.
A `DispatchedTuple` is like a dictionary, except

- the keys are **instances of types**
- they are backed by tuples, so they are GPU-friendly
- multiple keys are allowed (except for `DispatchedTupleSet`)
- each unique key returns a tuple of values given by that key (in order)

All `AbstractDispatchedTuple`s take a `Tuple` of `Pair`s, where the `first` field of the `Pair` (the "key") is **an instance of the type you want to dispatch on**. The `second` field of the `Pair` is the quantity (the "value", which can be anything) returned by `dispatch(::AbstractDispatchedTuple, key)`, the one user-facing method exported by DispatchedTuples.jl.

DispatchedTuples.jl has several user-facing types:

- `DispatchedTuple` - a dispatched tuple (example below)

- `DispatchedTupleSet` - a dispatched tuple set-- duplicate keys are not allowed. An error is thrown when `dispatch` is called with a duplicate key.

- `DispatchedTupleDict` - a dispatched tuple dict-- duplicate keys are allowed, but `dispatch` returns value from the last non-unique key.

`DispatchedTuple` takes a `Tuple` of `Pair`s, where the `first` field of the `Pair` (the "key") is **an instance of the type you want to dispatch on**. The `second` field of the `Pair` is the quantity (the "value", which can be anything) returned by `dispatch`.

## Example

Expand Down
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ For convenience, `DispatchedTuple` can alternatively take a `Tuple` of two-eleme
DispatchedTuples.AbstractDispatchedTuple
DispatchedTuples.DispatchedTuple
DispatchedTuples.DispatchedTupleSet
DispatchedTuples.DispatchedTupleDict
DispatchedTuples.dispatch
```
91 changes: 83 additions & 8 deletions src/DispatchedTuples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ module DispatchedTuples

import Base

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

export DispatchedTuple
export DispatchedTupleSet
export DispatchedTupleDict

struct NoDefaults end

Expand All @@ -14,6 +18,10 @@ dispatched tuples.
"""
abstract type AbstractDispatchedTuple{T <: Tuple, D} end

#####
##### DispatchedTuple
#####

"""
DispatchedTuple(tup[, default_value])
Expand Down Expand Up @@ -49,8 +57,6 @@ struct DispatchedTuple{T,D} <: AbstractDispatchedTuple{T, D}
end
end

first_eltype(::Type{Tuple{T, V}}) where {T,V} = T

"""
dispatch(::DispatchedTuple, type_instance)
Expand Down Expand Up @@ -88,11 +94,9 @@ end
expr
end

Base.isempty(dt::AbstractDispatchedTuple) = Base.isempty(dt.tup)
Base.length(dt::AbstractDispatchedTuple) = Base.length(dt.tup)
Base.map(f, dt::AbstractDispatchedTuple) = Base.map(f, dt.tup)
Base.keys(dt::AbstractDispatchedTuple) = map(x->x[1], dt)
Base.values(dt::AbstractDispatchedTuple) = map(x->x[2], dt)
#####
##### DispatchedTupleSet
#####

"""
DispatchedTupleSet(tup[, default_value])
Expand Down Expand Up @@ -157,7 +161,78 @@ end
end
end

#####
##### DispatchedTupleDict
#####

"""
DispatchedTupleDict(tup[, default_value])
Similar to `DispatchedTuple`, except:
- keys need not be unique, _but_ only the last key is used
- returns the value, and not a tuple of values.
"""
struct DispatchedTupleDict{T,D} <: AbstractDispatchedTuple{T, D}
tup::T
default::D
function DispatchedTupleDict(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(::DispatchedTupleDict, type_instance)
Dispatch on the [`DispatchedTupleDict`](@ref), based
on the instance of the input type `type_instance`.
"""
@generated function dispatch(dt::DispatchedTupleDict{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
push!(expr.args, :(throw(error("No method dispatch defined for type $T"))))
end
return expr
end

@generated function dispatch(dt::DispatchedTupleDict{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)
else
return expr
end
end

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

# Interface / extending:
Base.isempty(dt::AbstractDispatchedTuple) = Base.isempty(dt.tup)
Base.length(dt::AbstractDispatchedTuple) = Base.length(dt.tup)
Base.map(f, dt::AbstractDispatchedTuple) = Base.map(f, dt.tup)
Base.keys(dt::AbstractDispatchedTuple) = map(x->x[1], dt)
Base.values(dt::AbstractDispatchedTuple) = map(x->x[2], dt)

# Helper
first_eltype(::Type{Tuple{T, V}}) where {T,V} = T

end # module
35 changes: 35 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,41 @@ end
@test isempty(dt) == isempty(tup)
end

#####
##### DispatchedTupleDict's
#####

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

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

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

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

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

#####
##### DispatchedTupleSet's
#####
Expand Down

2 comments on commit 5ab1763

@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/31379

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.3 -m "<description of version>" 5ab1763360b59890b5ca2dfd528f79e3e509276b
git push origin v0.1.3

Please sign in to comment.