CanonicalTraits.jl icon indicating copy to clipboard operation
CanonicalTraits.jl copied to clipboard

Mechanism to implement based on the trait

Open MasonProtter opened this issue 4 years ago • 8 comments

So I think what @AriMKatz was getting at in https://github.com/thautwarm/CanonicalTraits.jl/issues/7#issuecomment-574924912 was that they want a way to implement a trait based on behaviour instead of on datatype.

That is, suppose we have two traits TraitA and TraitB

@trait TraitA{T} begin
    ...
end

@trait TraitB{T} begin
    ...
end

Ari wants to be able to write

@trait TraitC{T} begin
    f :: [T] => T
end

@implement TraitC{T} where {hastrait(T, TraitA)} begin
    f(x::T) = ...
end

@implement TraitC{T} where {hastrait(T, TraitB)} begin
    f(x::T) = ...
end

So the implementation of TraitC{T} will depend on if T has the trait TraitA or if it has TraitB. Did I get that right Ari?

I think this is a sensible thing to try and support, but it might be tricky to implement.

MasonProtter avatar Feb 15 '21 20:02 MasonProtter

Yup! Thanks

AriMKatz avatar Feb 15 '21 20:02 AriMKatz

Sorry, I didn't realized this is already supported. Use:

@implement TraitC{T} <: TraitA{T} begin
    f(x::T) = ...
end

thautwarm avatar Dec 10 '21 06:12 thautwarm

And I think we need a better way to access methods from parent traits.

thautwarm avatar Dec 10 '21 06:12 thautwarm

@trait TraitA{T} begin
           f :: [T] => Vector{T}
       end
@implement Trait{Int} begin
      f(x) = Int[x]
end

@trait HasLength{T} begin
     len :: [T] => Int
end

@implement (HasLength{T} where T) <: TraitA{T} begin
     len(x) = length(f(x))
end

julia> len(1) # len(1) = length(f(1)) = length([1]) = 1
1

thautwarm avatar Dec 10 '21 06:12 thautwarm

This doesn't work for multiple traits though. I.e. what's desired is that f would dispatch on whether or not x has the trait Add1 or Add2.

using CanonicalTraits

@trait Add1{T} begin
    add1 :: [T] => T
    add1(x) = x + 1
end

@trait Add2{T} begin
    add2 :: [T] => T
    add2(x) = x + 2
end

@implement Add1{Int}

@implement Add2{Float64}

@trait F{T} begin
    f :: [T] => T
end

@implement (F{T} where T) <: Add1{T} begin
    f(x) = add1(x) + g(x)
end


@implement (F{T} where T) <: Add2{T} begin
    f(x) = add2(x) + h(x)
end

g(x) = (println("Called g"); 2)

h(x) = (println("Called h"); 1)
julia> f(1.0)
Called h
4.0

julia> f(1)
ERROR: Not implemented trait Add2 for (Int64).

MasonProtter avatar Dec 10 '21 19:12 MasonProtter

I see, the current implementation does not allow dispatching over super traits.

thautwarm avatar Dec 12 '21 05:12 thautwarm

I'd like to support it.

thautwarm avatar Dec 12 '21 05:12 thautwarm

I think I'm basically looking for something like this: https://www.fceia.unr.edu.ar/~mauro/pubs/cm-conf.pdf

AriMKatz avatar Mar 10 '22 13:03 AriMKatz