Skip to content

mrufsvold/DuckDispatch.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

97 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DuckDispatch.jl Duck Dispatch logo

Stable Dev Build Status Coverage Aqua

DuckDispatch.jl is an experimental package which attempts to make it easy to dispatch a method based on the behavior of a type, not its place in the type hierarchy. At a high-level, it allows the user to define a number of method signatures which constitute a DuckType. Then, any type which has an implementation for those methods can be wrapped in a Guise{D<:DuckType, T}. This Guise type is then hooked into the normal Julia dispatch machinery.

Why?

It often does not matter if an input is of a specific type (like a vector, a channel, or a set); it matters that it is has a certain functionality (like iterable). While creating a method with a completely generic argument will work, it also provides no guarantees that the input will have the necessary methods.

By dispatching on a DuckType, we get:

  1. compile time guarantees that there won't be any method errors for the Behaviors defined for the DuckType
  2. helpful errors for calls to a method that is not defined for the DuckType
  3. a method signature that is more informative about the meaning of is arguments

The Basics

To define an Iterable DuckType, we can do the following:

using DuckDispatch
@duck_type struct Iterable{T}
    function Base.iterate(::This)::Union{Nothing, Tuple{T, <:Any}} end
    function Base.iterate(::This, ::Any)::Union{Nothing, Tuple{T, <:Any}} end
    @narrow T -> Iterable{eltype(T)}
end

Now, we can create a new function that dispatches on this DuckType:

@duck_dispatch function my_collect(arg1::Iterable{T}) where {T}
    v = T[]
    for x in arg1
        push!(v, x)
    end
    return v
end

using Test
@test my_collect((1,2)) == [1,2]
@test my_collect(1:2) == [1,2]
@test my_collect((i for i in 1:2)) == [1,2]

Iterable is pretty limited without length. We can compose it with some new behaviors to build a more feature-rich DuckType!

@duck_type struct FiniteIterable{T} <: Union{Iterable{T}}
    function Base.length(::This)::Int end
    @narrow T -> FiniteIterable{eltype(T)}
end
@duck_dispatch function my_collect(arg1::FiniteIterable{T}) where {T}
    return T[x for x in arg1]
end

More Information

See the developer documentation for more information the internals of this package.

About

If it quacks like a duck... dispatch on it!

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages