Coder Social home page Coder Social 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

PrettyPrint Behaviors

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

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)

`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.

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

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.

Remove foldable from dispatch

Base.@assume_effects :foldable function dispatch_behavior(

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

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 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

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.

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.

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.