# Multiple dispatch, but for structs?

**URL:** <https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221>\
**Category:** General Usage\
**Created:** [January 24, 2024, 9:46pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221 "2024-01-24T21:46:12Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![mkoculak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkoculak/32/28310_2.png) [@mkoculak](https://discourse.julialang.org/u/mkoculak)\
**Post date:** [January 24, 2024, 9:46pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/1 "2024-01-24T21:46:12Z")

</div>

I was thinking about having an option to specify a “fallback” usage for structs, something that seems to me like a reversed multiple dispatch?

Let’s say I have a struct:

```julia
struct Foo
    x::String
    y::Array
end

```

And I would like to specify, that if someone would use a function on it that does not have specialized version for `Foo` but has for e.g. `AbstractArray` it would then use `Foo.y` instead of `Foo`.  
I was not sure how to call this, so I haven’t found much with searching Discourse/Google. Can we do it somehow in Julia?

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [January 24, 2024, 10:11pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/2 "2024-01-24T22:11:10Z")

</div>

I think your question here relates to “forwarding” or “delegating” functions to a field when using a “composition” design pattern.

There have been numerous discussion about this here on discourse; maybe the most comprehensive list can be found at the end of the `ReusePatterns` readme:

> **[GitHub - gcalderone/ReusePatterns.jl: Implement composition and concrete...](https://github.com/gcalderone/ReusePatterns.jl)**
>
> Implement composition and concrete subtyping in Julia. - GitHub - gcalderone/ReusePatterns.jl: Implement composition and concrete subtyping in Julia.

As to “how”, that same `ReusePatterns` package provides this kind of feature, and its readme also gives a detailed list of packages that provide related features.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 25, 2024, 4:46pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/3 "2024-01-25T16:46:22Z")

</div>

> [@mkoculak](#):
>
> And I would like to specify, that if someone would use a function on it that does not have specialized version for `Foo` but has for e.g. `AbstractArray` it would then use `Foo.y` instead of `Foo`.

Although you can dispatch on the type of the callable, even abstract ones, instead of specifying a `const` name, Julia disallows `(f::Function)(a::Foo) = f(a.y)` because it would allow call signatures too broadly, even paradoxically involving functions that aren’t multimethods. You would have to specify the functions individually, like `bar(a::Foo) = bar(a.y)` or a general fallback `bar(a) = bar(a.y)`. The forwarding macros can make such methods in a nicer syntax especially for multiple listed functions, and some can search and implement all applicable functions like the previously linked ReusePatterns, though with common-sense limitations. AFAIK these macros all share the limitation of accessing a specific field name, so they can’t do something broader like `bar(a) = bar(getarray(a))`. They also cannot automatically rewrap types, like `bar(::Foo)::Foo` to reflect `bar(::Array)::Array`, because that sort of processing will be different for different types. For example, if you matrix-multiply 2 `Foo`s by forwarding to the arrays at the `y` field, what do you do about the 2 strings at the `x` field?

Personally I prefer to implement `getarray(a::Foo) = a.y` with a fallback `getarray(a::AbstractArray) = a` (if the types are semantically views of an array, then `Base.parent` should be used and already has that fallback), and I would call `bar(getarray(b))` instead of adding methods to make `bar` work on types other than arrays. Although extending functions on new types is normal, I don’t often find forwarding alone to be enough justification. That’s just a subjective style, and it’s not less work than forwarding, especially with those convenient macros.

---

<div class="post-metadata">

**Author:** ![mkoculak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkoculak/32/28310_2.png) [@mkoculak](https://discourse.julialang.org/u/mkoculak)\
**Post date:** [January 25, 2024, 6:26pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/4 "2024-01-25T18:26:02Z")

</div>

Yes, forwarding seems to be exactly what I had in mind, thanks!

@Benny not sure if we are thinking of the same scenario - my use case would be having a data structure that would behave as a dense array unless stated otherwise, so a user could call e.g. `eigen` and just get the result. But the obstacle would be that I don’t know which functions user would want to use, so could not extend them in my code.  
So the question is can I make the struct compatible with array functions while only having control of the said struct and not implementing any specialized functions.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 25, 2024, 7:49pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/5 "2024-01-25T19:49:04Z")

</div>

> [@mkoculak](#):
>
> not implementing any specialized functions.

Well, no, you do have to implement methods on your type to make anything happen, that’s what forwarding does, too. It’s ambiguous what you mean by “behave as a dense array”; that could be something different from forwarding.

> [@mkoculak](#):
>
> can I make the struct compatible with array functions while only having control of the said struct

If you want your type to directly represent a dense array, not just contain one, I’ll suggest 2 options: 1) if the elements are stored contiguously, which `y::Array{T,N}`\* would be, then subtype `struct Foo{T,N} <: DenseArray{T,N}`, or 2) if dense storage isn’t critical, subtype `struct Foo{T,N} <: AbstractArray{T,N}` (note that `DenseArray` is a subtype of `AbstractArray`). \*The parameters will be important for dispatching to existing `AbstractArray` methods, and you should match it to the `y` field. Whichever option you take, you should then implement some methods in the [AbstractArray interface](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array) to let base `AbstractArray` methods work on your type, and yes they should forward to the contained `y::Array{T,N}` in some form. Which optional interface methods to implement depends on your use case (if you don’t want mutation, you shouldn’t implement `setindex!`), and some of them have good default implementation (like `iterate` being based on the required `getindex`). Earlier I said that I try to leave a function alone and explicitly call a getter instead of extending that function just for forwarding, and implementing an interface used by that function and likely many more others is another way to leave those functions alone, except you do extend the smaller set of interface methods.

Also, if you decide to go the actual array route, then you should implement `Base.parent(a::Foo) = a.y` if you want a getter, don’t make your own.

---

<div class="post-metadata">

**Author:** ![mkoculak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkoculak/32/28310_2.png) [@mkoculak](https://discourse.julialang.org/u/mkoculak)\
**Post date:** [January 28, 2024, 11:35pm UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/6 "2024-01-28T23:35:53Z")

</div>

Yeah, so I guess I was thinking about something that would behave as subtyping, but for multiple types, not only `AbstractArray`, but also e.g. `AbstractString`. But the more I thought about it, the more apparent it was, that it would generate a lot of ambiguities.  
I actually tried subtyping from `AbstractArray` but at the time it didn’t fully work for me (but it was pretty long ago and I must have done something wrong there). Will have to give it another try.

On the side note, it seems to me, that forwarding would be now doing a very similar thing to package extensions, as you have to generate methods on your own. Is there any advantage to forwarding over them?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 29, 2024, 12:29am UTC](https://discourse.julialang.org/t/multiple-dispatch-but-for-structs/109221/7 "2024-01-29T00:29:55Z")

</div>

> [@mkoculak](#):
>
> would behave as subtyping, but for multiple types, not only `AbstractArray`, but also e.g. `AbstractString`. But the more I thought about it, the more apparent it was, that it would generate a lot of ambiguities.

Yeah multiple supertypes don’t mix well with multimethods, so Julia doesn’t have them. You can sort of get around it with multiple traits, where a type gets 1 trait per trait-method. But honestly for this I would just dispatch on `a.x` or `a.y`, “composition over inheritance” as it’s said.

> [@mkoculak](#):
>
> it seems to me, that forwarding would be now doing a very similar thing to package extensions, as you have to generate methods on your own. Is there any advantage to forwarding over them?

I don’t see the parallel you are drawing. In both you are defining methods, yes, but that seems true for implementing anything. Package extensions are about mixing functions and types from multiple packages in methods that get loaded automatically when all of said packages are loaded. You could make forwarding methods in them, but that’s an orthogonal option.
