Issam T.
Issam T.

Reputation: 1697

Check if a type implements an interface in Julia

How to check that a type implements an interface in Julia?

For exemple iteration interface is implemented by the functions start, next, done.

I need is to have a specialization of a function depending on wether the argument type implements a given interface or not.

EDIT

Here is an example of what I would like to do.

Consider the following code:

a = [7,8,9]
f = 1.0    
s = Set()
push!(s,30)
push!(s,40)

function getsummary(obj)
  println("Object of type ", typeof(obj))
end

function getsummary{T<:AbstractArray}(obj::T)
  println("Iterable Object starting with ", next(obj, start(obj))[1])
end

getsummary(a)
getsummary(f)
getsummary(s)

The output is:

Iterable Object starting with 7
Object of type Float64
Object of type Set{Any}

Which is what we would expect since Set is not an AbstractArray. But clearly my second method only requires the type T to implement the iteration interface.

my issue isn't only related to the iteration interface but to all interfaces defined by a set of functions.

EDIT-2

I think my question is related to

https://github.com/JuliaLang/julia/issues/5

Since we could have imagined something like T<:Iterable

Upvotes: 4

Views: 1306

Answers (2)

Fengyang Wang
Fengyang Wang

Reputation: 12051

Typically, this is done with traits. See Traits.jl for one implementation; a similar approach is used in Base to dispatch on Base.iteratorsize, Base.linearindexing, etc. For instance, this is how Base implements collect using the iteratorsize trait:

"""
    collect(element_type, collection)

Return an `Array` with the given element type of all items in a collection or iterable.
The result has the same shape and number of dimensions as `collection`.
"""
collect{T}(::Type{T}, itr) = _collect(T, itr, iteratorsize(itr))

_collect{T}(::Type{T}, itr, isz::HasLength) = copy!(Array{T,1}(Int(length(itr)::Integer)), itr)
_collect{T}(::Type{T}, itr, isz::HasShape)  = copy!(similar(Array{T}, indices(itr)), itr)
function _collect{T}(::Type{T}, itr, isz::SizeUnknown)
    a = Array{T,1}(0)
    for x in itr
        push!(a,x)
    end
    return a
end

See also Mauro Werder's talk on traits.

I would define a iterability(::T) trait as follows:

immutable Iterable end
immutable NotIterable end
iterability(T) =
    if method_exists(length, (T,)) || !isa(Base.iteratorsize(T), Base.HasLength)
        Iterable()
    else
        NotIterable()
    end

which seems to work:

julia> iterability(Set)
Iterable()

julia> iterability(Number)
Iterable()

julia> iterability(Symbol)
NotIterable()

Upvotes: 6

Gnimuc
Gnimuc

Reputation: 8566

you can check whether a type implements an interface via methodswith as follows:

foo(a_type::Type, an_interface::Symbol) = an_interface ∈ [i.name for i in methodswith(a_type, true)]

julia> foo(EachLine, :done)
true

but I don't quite understand the dynamic dispatch approach you mentioned in the comment, what does the generic function looks like? what's the input & output of the function? I guess you want something like this?

function foo(a_type::Type, an_interface::Symbol)
    # assume bar baz are predefined
    if an_interface ∈ [i.name for i in methodswith(a_type, true)]
        # call function bar
    else 
        # call function baz
    end
end

or some metaprogramming stuff to generate those functions respectively at compile time?

Upvotes: 1

Related Questions