# Is a parametric Collection trait useful?

**URL:** <https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941>\
**Category:** General Usage\
**Created:** [November 8, 2023, 12:31pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941 "2023-11-08T12:31:38Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sebastiaan](https://avatars.discourse-cdn.com/v4/letter/s/48db29/32.png) [@Sebastiaan](https://discourse.julialang.org/u/Sebastiaan)\
**Post date:** [November 8, 2023, 12:31pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941/1 "2023-11-08T12:31:38Z")

</div>

I’ve been writing Julia for a while, and I find that I often want to have a function dispatch on the type that’s in a collection, where I don’t really care what type of collection is used, be it a set or a vector or a tuple. I think I’ve found a solution, and I wanted to ask whether that solution is something I should actually use.

So I wanted it to work something like this:

```julia
foo(thing) = x
foo(coll::Collection{<:Number}) = coll .* 2
foo(coll::Collection{<:AbstractString}) = join(coll, ", "))

```

I looked for whether this was a thing, but I couldn’t find it. So I implemented it myself, using the Holy traits trick with parametric trait types, like so:

```julia
abstract type CollectionTrait{T} end
struct NonCollection{T} <: CollectionTrait{T} end
struct Collection{T} <: CollectionTrait{T} end

# Everything by default is not a collection
collection_trait(::Type{T}) where T = NonCollection{T}()

# Vectors, Sets, and Tuples are all collections
collection_trait(::Type{<:AbstractVector{T}}) where T = Collection{T}()
collection_trait(::Type{<:AbstractSet{T}}) where T = Collection{T}()
collection_trait(::Type{<:Tuple{Vararg{T}}}) where T = Collection{T}()

# dispatch to the specific methods
foo(x::T) where T = foo(collection_trait(T), x)

# The methods, where we now don't need to care what the specific collection type is
foo(::Collection{Symbol}, coll) = println("A collection of Symbols.")
foo(::Collection{<:Number}, coll) = println("A collection of Numbers.")
foo(::NonCollection, thing) = println("Just a thing, not a collection.")

```

Where on the last lines you see something that’s pretty much what I wanted.

So now my question is: should I use this? Has someone perhaps done a better job of implementing something like this and should I use their solution instead? Or is this an abstraction that I’d better not make, and should I just dispatch on `Any` and see if looping over the values works?

---

<div class="post-metadata">

**Author:** ![Liozou](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/liozou/32/35118_2.png) [@Liozou](https://discourse.julialang.org/u/Liozou)\
**Post date:** [November 8, 2023, 2:02pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941/2 "2023-11-08T14:02:03Z")

</div>

Why not simply do `foo(collection) = bar(collection, eltype(collection))` with `bar` containing the implementation that may depend on both the type of `collection` and the type of its elements?

Basically, I believe that your trait may already be implemented as `eltype`. But please let me know if I misunderstood something!

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [November 8, 2023, 2:03pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941/3 "2023-11-08T14:03:02Z")

</div>

Half of what you want already exists, in `Base.IteratorEltype`. If it returns a `HasEltype` you can call `eltype` on the object.

But some objects are collections without known eltypes, or don’t implement iteration. Its a pretty loose “interface”.

---

<div class="post-metadata">

**Author:** ![Sebastiaan](https://avatars.discourse-cdn.com/v4/letter/s/48db29/32.png) [@Sebastiaan](https://discourse.julialang.org/u/Sebastiaan)\
**Post date:** [November 8, 2023, 3:05pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941/4 "2023-11-08T15:05:40Z")

</div>

That can work, but it does have different behaviour. With

```julia
bar(coll) = bar(coll, eltype(coll))
bar(coll, ::Type{Symbol}) = "A collection of symbols."
bar(coll, ::Type{<:Number}) = "A collection of numbers."
bar(thing, ::Type{<:Any}) = "Just a thing."
bar(thing, ::Type{Symbol}) = "Just a Symbol."

```

we get that `bar([1, :a])` returns `"Just a thing."` (while with my trait that gives a `MethodError`), `bar(3)` returns `"A collection of numbers"`, because apparently basically everything has an eltype which can just be itself, and `bar(:a)` returns `"Just a thing."` because `eltype(Symbol) == Any`.

With my `CollectionTrait` I can limit what’s a collection and what isn’t, while it can be extended by just adding another type.

I may have misinterpreted what you meant, though, of course.

---

<div class="post-metadata">

**Author:** ![Liozou](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/liozou/32/35118_2.png) [@Liozou](https://discourse.julialang.org/u/Liozou)\
**Post date:** [November 8, 2023, 3:18pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941/5 "2023-11-08T15:18:55Z")

</div>

I had missed that point, I thought you actually wanted it to generalize automatically to all collections (well, all that implement the [documented interface](https://docs.julialang.org/en/v1/manual/interfaces/) as @Raf mentionned).

But if you want to be able to constrain the kind of collections to a known set, like only `AbstractVector`, `AbstractSet` and `NTuple`, and treat everything else differently, then I actually think your initial solution works pretty well!

---

<div class="post-metadata">

**Author:** ![Liozou](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/liozou/32/35118_2.png) [@Liozou](https://discourse.julialang.org/u/Liozou)\
**Post date:** [November 8, 2023, 3:29pm UTC](https://discourse.julialang.org/t/is-a-parametric-collection-trait-useful/105941/6 "2023-11-08T15:29:49Z")

</div>

> [@Sebastiaan](#):
>
> because apparently basically everything has an eltype which can just be itself, and `bar(:a)` returns `"Just a thing."` because `eltype(Symbol) == Any`.

(just a side note: actually it’s the other way round: everything has `eltype` equal to `Any` except collections, as well as numbers that have `eltype(x) = typeof(x)`. That’s because numbers are considered themselves to be collections: you can iterate on a number and it will be considered a 1-sized collection containing only itself)
