thautwarm/CanonicalTraits.jl

API constraints for method arguments

Opened this issue · 5 comments

Hi Taine,

I'd like to better understand the limitations of this approach, and any workarounds.

In the docs you say,

Due to the limitations of dynamic language, the type parameters occurred in trait signature should occur in the argument of each trait methods.

I had some bugs in an early attempt to use this, so your point on this is very helpful. But then in the README example you have,

@trait VecSpace{F, V} >: InnerProd{F, V} where
  {F = V2F(V)} begin
  dot :: [V, V] => F
end

Doesn't dot violate this constraint? Is it the functional dependency that makes it still work? Do you have any general suggestions for using this package, especially wrt any interactions with abstract types?

Hi, Chad. Yes, it's the functional dependencies that help in this case.
For the suggestions, there might be lots, I'll make some comments on this later.
For now, as you mentioned functional dependencies, I'd say
If the arguments do not provide sufficient information for inference, try to add more functional dependencies if legally. Say, if the trait have 2 parameters A, B, some methods have one in arguments, and some have another, and the 2 type params can decide each other, you'd better add two rules to infer A => B and B => A

Another suggestion is, if you want to compat v1.0, you might not use default methods currently(v1.0+ is okay). This is an issue of Julia itself, which I haven't checked if solved now.

Also, @implement! is fast and seems to guarantee the zero-cost instance lookup for arbitrary type stable cases, but it uses generated functions, and thus if you want to call a method to check if some traits are implemented for some types, you will lose the chance to implement these traits for these types later, because the generated functions are already generated(functions throw not implemented exception)for these types.

Ok thanks, this is really helpful :)

What about extending a function from another library to dispatch on traits? Is there a way to do this in "standard" Julia style? Or does using traits at all mean every dispatch needs to be in terms of traits?

What about extending a function from another library to dispatch on traits?

I think you can create a separate module, define the trait there, and for the function from another library, just add an overload whose implementation is the trait function.

func(args...) = trait_func(args...)

Is there a way to do this in "standard" Julia style?

I guess SimpleTriaits.jl does well in this style?

Or does using traits at all mean every dispatch needs to be in terms of traits?

No, but don't overwrite the dispatch used by CanonicalTraits.

For instance,

julia> function relation end
relation (generic function with 0 methods)

julia> relation(::Type{Int}) = Int
relation (generic function with 1 method)

julia> @trait Add1{Arg, Ret} where {Ret = relation(Arg)} begin
             add1 :: [Arg] => Ret
       end

julia> methods(add1)
# 1 method for generic function "add1":
[1] add1(add1::Arg) where Arg in Main

julia> add1(1)
2

julia> add1(x::Float64) = x + 1
add1 (generic function with 2 methods)

julia> add1(1.0)
2.0

julia> add1(x::String) = x * "1"
add1 (generic function with 3 methods)

julia> add1("a")
"a1"