GithubHelp home page GithubHelp logo

duckdispatch.jl's Introduction

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.

duckdispatch.jl's People

Contributors

dependabot[bot] avatar mrufsvold avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

duckdispatch.jl's Issues

Allow Compile-Time Opt-in for Specific Types

Right now, the dispatch machinery has to check hasmethod or methods for a new type to see if it quacks_like a method signature.

We could allow the user to mark a type as adhering to a specific DuckType at the top level. Which would let us short circuit those checks. It might look like

@mark Container{T} => Vector{T} where {T}
# lowers to 
quacks_like(::Type{<:Container}, ::Type{<:Vector}) = true
narrow(::Type{<:Container}, ::Vector{T}) where {T} = Container{T}
wrap(::Type{C}}, x::V) where {T, V<:Vector{T}, C <: Container{T}} = Guise{C,V}(x)

Remove foldable from dispatch

Base.@assume_effects :foldable function dispatch_behavior(

This can't be assumed because it calls a user function

Handle Conflicting Behaviors

Right now, you could compose two DuckTypes with the same signature for a Behavior. That is somewhat problematic because we'd have to decide which to use. But more importantly, we could have different required return types.

I think the best way to handle this would be to require that the highest level DuckType defines it's own version of the conflicting methods.

It does become a little problematic to parse what implies would do if the higher DuckType has the same behavior but potentially a different return type.

Test Fallback Overwrite Behavior

Theoretically, if there is already a method with the same number of arguments and they are all ::Any, @duck_dispatch should throw an error preventing it from being overwritten.

There is no test case for this yet.

Support `This` as a parameter

Need to handle supporting something like Iterable{This} as a parameter for a Behavior. It is complicated because we need to be able to extract and replace this from the parameters of arbitrary types.

Don't re-narrow if the user provided a type param

I think right now, if you notate an argument with ::Iterable{Int}, the Int isn't binding because the dispatch testing always calls narrow. I think I need to have it call narrow and then test if all the type params are subtypes of the original type params

Add `@check`

Some attributes of a type are not discernible from the MethodTable. An easy example is that iterable is defined for all Numbers, but Numbers shouldn't be dispatched on a DuckType Iterable. So @check would allow for specific handling of cases like this.

Support union type annotations in method

We should be able to construct a DuckType on the fly with @duck_dispatch f(x::Union{Dtype1, Dtype2}) where we build a new DuckType with no new Behaviors, just the Meet of the member DuckTypes

PrettyPrint Behaviors

A prerequisite to a lot of good logging and errors is a pretty printed form of behaviors and DuckTypes

`DuckType` Return Asserts

Right now, any return type annotations on behaviors are turned into ordinary return type annotations. However, if the user wants to assert that the return type is a DuckType, this won't work.

For example, you might want to declare

@duck_type struct Container{T, N}
    function similar(This, ::Int...)::Container{T, Any)
end

But similar won't return a literal `Container`. It will return some AbstractArray which fails a naive type assert.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.