mauro3 / SimpleTraits.jl

Simple Traits for Julia
MIT License
158 stars 14 forks source link

Associated types #45

Open mauro3 opened 7 years ago

mauro3 commented 7 years ago

The AbstractArray interface also encompasses the meaning of the type parameters: MyArray{T,N} has eltype T and dim N. A trait LikeArray ought to contain these two type parameters also, which in this context are associated types (at least Rust uses this term). Maybe this could be written as TraitAbstractArray{TT,2}(SomeTypeWhichIsAlsoArrayLike{P1,P2,TT}), which would mean that SomeTypeWhichIsAlsoArrayLike{P1,P2,T} belongs to the trait and that it has associated types eltype==TT and ndims==2. Note this syntax is different to the current syntax on master, where the type-parameters are occupied by the types fulfilling (or not) the trait.

Not sure this is SimpleTraits material but I thought I better write it down somewhere before I forget.

mauro3 commented 7 years ago

I prototyped this in https://github.com/mauro3/SimpleTraits.jl/tree/m3/associated-types, which includes an example:

using SimpleTraits

# This is a trait of one type `Ar` with two associated types `T,N`:
@traitdef LikeArray{T,N}(Ar)
# NOTE: the syntax change: before the types in question where in the curly
# brackets and there where no associated types.  Now the type in question is `(Ar)`.

# Sure all Arrays are like arrays:
@traitimpl LikeArray{T,N}(Array{T,N}) where {T,N}
# Note that the trait has the T,N parameters just like Array.

abstract type MAbs end
struct MyType{O,U,N,T} <: MAbs
    o::O
    u::U
    ar::Array{T,N}
end
# Implement array interface...

# add it to trait:
@traitimpl LikeArray{T,N}(MyType{O,U,N,T}) where {O,U,N,T}
# Note how the MyType parameters get mapped onto the associated types

# use it
import Base: eltype, ndims
@traitfn ndims{T,N}(::::LikeArray{T,N}) = (println("Hi"); N)
ndims(MyType(4,5,[3])) # => "Hi" 1
ndims(MyType(4,5,rand(1,1))) # => "Hi" 2
ndims([1]) # => 1 (this does not go via the trait function as normal dispatch
           #       takes presendce)

@traitfn eltype{T}(::::LikeArray{T}) = T
eltype(MyType(4,5,[3])) # => Int
eltype(MyType(4,5,rand(1,1))) # => Float64
eltype([1]) # => Int

@traitfn two_dim_only{T}(::::LikeArray{T,2}) = "I have ndim==2"
@traitfn two_dim_only{T}(::::(!LikeArray{T,2})) = "I have ndim!=2"

two_dim_only(rand(2,2)) # => "I have ndim==2"
# this sadly doesn't work, needs some more thoughs:
two_dim_only(MyType(4,5,[3])) # => error

This is essentially the {T} in what Keno requested here (but of course without the bit which allows to specify the trait in terms of implemented interface).

I think this is pretty sweet. Is there any use for this for people who actually use SimpleTraits? CC: @timholy, @sbromberger, @ChrisRackauckas, @davidanthoff, @JeffreySarnoff (sorry about the indiscriminate CCing, I got a bit excited!)

JeffreySarnoff commented 7 years ago

Thanks for asking. This feels like a step that brings not-yet-builtin traits closer to their Julian realization.

sbromberger commented 7 years ago

Sorry for the delayed response. I'm still trying to wrap my head around this proposal :)

mauro3 commented 7 years ago

No worries, no need for quick response. In a nutshell it would allow for traits to have parameters just like abstract types, where things like T,N in AbstractArray{T,N} can be useful for dispatch.

mauro3 commented 6 years ago

Would be interesting to discuss/hack this at JuliaCon 2018, if anyone is interested.

JeffreySarnoff commented 6 years ago

I like parameters.