# f(x::MyType{T, S}) where (T || S) \<: Foo?

**URL:** <https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581>\
**Category:** General Usage\
**Created:** [December 13, 2022, 12:51am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581 "2022-12-13T00:51:36Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![cjwyett](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjwyett/32/13345_2.png) [@cjwyett](https://discourse.julialang.org/u/cjwyett)\
**Post date:** [December 13, 2022, 12:51am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/1 "2022-12-13T00:51:36Z")

</div>

Hi,

I have a type where I want a different dispatch if one of the parameters is Foo.

```julia
abstract type FooBarBaz end

struct Foo <: FooBarBaz end 
struct Bar <: FooBarBaz end
struct Baz <: FooBarBaz end

struct MyType{T<:FooBarBaz, S<:FooBarBaz} end

```

The cleanest (legal) way I can think of doing this is

```julia
f(x::MyType{Foo}) = ...
f(x::MyType{T,Foo}) where T = ...

```

But I end up in dispatch hell pretty quickly with more parameters.

Is something nice for this?

Many thanks!

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [December 13, 2022, 2:10am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/2 "2022-12-13T02:10:38Z")

</div>

I suppose you could make a `hasfooparam` method so you only have to waddle through dispatch hell once. Do you anticipate having a large number of type parameters?

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 13, 2022, 2:13am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/3 "2022-12-13T02:13:19Z")

</div>

```julia
f(x::MyType{<:Any,Foo}) = ...

```

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [December 13, 2022, 2:26am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/4 "2022-12-13T02:26:05Z")

</div>

also `Union{Foo, Bar} === Union{Bar, Foo}`, if that helps

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 13, 2022, 2:28am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/5 "2022-12-13T02:28:29Z")

</div>

For something a bit more automated, how about this?

```julia
julia> fooey = [:(MyType{$(fill(:(<:Any), n)...), Foo}) for n=0:4]
5-element Vector{Expr}:
 :(MyType{Foo})
 :(MyType{<:Any, Foo})
 :(MyType{<:Any, <:Any, Foo})
 :(MyType{<:Any, <:Any, <:Any, Foo})
 :(MyType{<:Any, <:Any, <:Any, <:Any, Foo})

julia> const FooTypes = eval(:(Union{$(fooey...)}))

```

Then you could do

```julia
f(x::FooTypes) = ...

```

It’s also possible to use the `@generated` macro to make generated functions…

```julia
@generated f(x) = begin
    if #= logic to detect if `Foo` is a parameter of the type `x` =#
        :(#= function code =#)
    else
        :(#= other function code =#)
    end 
end

```

Edit:

Almost forgot. You can also use switching logic like this, if the performance is acceptable:

```julia
f(x::T) where T<:MyType = begin
    if Foo ∈ T.parameters
        #= ... =#
    else
        #= ... =#
    end
end 

```

The downside being that you lose function signature discoverability.

---

<div class="post-metadata">

**Author:** ![cjwyett](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjwyett/32/13345_2.png) [@cjwyett](https://discourse.julialang.org/u/cjwyett)\
**Post date:** [December 13, 2022, 4:59am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/6 "2022-12-13T04:59:01Z")

</div>

I don’t think this would work as `MyType{<:Any, Foo, <:Any}` would be rejected

Edit

Oh no! I think you’re right about this one my bad

---

<div class="post-metadata">

**Author:** ![cjwyett](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjwyett/32/13345_2.png) [@cjwyett](https://discourse.julialang.org/u/cjwyett)\
**Post date:** [December 13, 2022, 5:11am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/7 "2022-12-13T05:11:05Z")

</div>

Indeed I do, this is the way I’m handling it now.

```julia
hasfoo(x::MyType{T,S,V}) where {T,S,V} = any(map(param -> param <: Foo, (T,S,V)))

```

As you can imagine, this does not scratch the itch. `if hasfoo(x)`ing all over the place is also rather unsightly.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [December 13, 2022, 6:20am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/8 "2022-12-13T06:20:05Z")

</div>

> [@cjwyett](#):
>
> `hasfoo(x::MyType{T,S,V}) where {T,S,V} = any(map(param -> param <: Foo, (T,S,V)))`

You can at least simplify the above to

```julia
hasfoo(x::MyType{T,S,V}) where {T,S,V} = any(p -> p <: Foo, (T,S,V))

```

`any` accepts a function as a first input, so that `any(f, itr)` is basically equivalent to `any(f(x) for x in itr)`.

Can you demonstrate how you are using `hasfoo` in a way that makes it unsatisfactory?

---

<div class="post-metadata">

**Author:** ![Jollywatt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jollywatt/32/202198_2.png) [@Jollywatt](https://discourse.julialang.org/u/Jollywatt)\
**Post date:** [December 13, 2022, 6:43am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/9 "2022-12-13T06:43:28Z")

</div>

Depending on your problem, you might find it appropriate to store the “hasfoo” status of the type as an extra Boolean type parameter which you can dispatch on. You may also write an inner constructor which calculates the extra type parameter so you don’t have to think about it.

For instance:

```julia
abstract type FooBarBaz end

struct Foo <: FooBarBaz end 
struct Bar <: FooBarBaz end
struct Baz <: FooBarBaz end

struct MyType{T<:FooBarBaz, S<:FooBarBaz, Fooey}
	MyType(T, S) = let hasfoo = T <: Foo || S <: Foo
		new{T,S,hasfoo}()
	end
end

const MyTypeFooey{T,S} = MyType{T,S,true}

f(::MyTypeFooey) = "this is a Fooey type"
f(::MyType) = "this is a not a Fooey type"

```

As a bonus, you can define type aliases which display nicely (see below) and make dispatch easier. I’ve defined `MyTypeFooey`, but you could also define `MyTypeFooless` and write methods for both types.

```julia
julia> fooless = MyType(Bar, Baz)
MyType{Bar, Baz, false}()

julia> f(fooless)
"this is a not a Fooey type"

julia> fooey = MyType(Bar, Foo) # notice how the type is represented
MyTypeFooey{Bar, Foo}()

julia> typeof(fooey)
MyTypeFooey{Bar, Foo} (alias for MyType{Bar, Foo, true})

julia> f(fooey)
"this is a Fooey type"

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 13, 2022, 7:19am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/10 "2022-12-13T07:19:00Z")

</div>

Ohhh I like this approach. I like it a lot. Take my updoot.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [December 13, 2022, 9:02am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/11 "2022-12-13T09:02:01Z")

</div>

To me this or condition sounds like the type parameters of `MyType` actually represent a set of types, i.e., their order would not matter. Thus, you could normalize them in the constructor by sorting your types:

```julia
abstract type FooBarBaz end

struct Foo <: FooBarBaz end 
struct Bar <: FooBarBaz end
struct Baz <: FooBarBaz end

Base.isless(x::Type{Foo}, y::Type{Bar}) = true
Base.isless(x::Type{Bar}, y::Type{Foo}) = false
Base.isless(x::Type{Foo}, y::Type{Baz}) = true
Base.isless(x::Type{Baz}, y::Type{Foo}) = false
Base.isless(x::Type{Bar}, y::Type{Baz}) = true
Base.isless(x::Type{Baz}, y::Type{Bar}) = false

struct MyType{T<:FooBarBaz, S<:FooBarBaz}
    function MyType(x, y)
        new{sort([typeof(x), typeof(y)])...}() 
    end
end

julia> MyType(Bar(), Foo())
MyType{Foo, Bar}()

```

Now, dispatching on the first argument is enough as any `Foo` type parameter would end up there.

---

<div class="post-metadata">

**Author:** ![cjwyett](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjwyett/32/13345_2.png) [@cjwyett](https://discourse.julialang.org/u/cjwyett)\
**Post date:** [December 13, 2022, 8:23pm UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/12 "2022-12-13T20:23:26Z")

</div>

The order does matter unfortunately.

---

<div class="post-metadata">

**Author:** ![cjwyett](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjwyett/32/13345_2.png) [@cjwyett](https://discourse.julialang.org/u/cjwyett)\
**Post date:** [December 13, 2022, 8:26pm UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/13 "2022-12-13T20:26:50Z")

</div>

This is nice, I think this approach may be better served by Holy traits?  
Still, none of these feel very Julia, I was hoping there was a neat little hack I could stick in the function signature. Seems like there isn’t after all.

---

<div class="post-metadata">

**Author:** ![cjwyett](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjwyett/32/13345_2.png) [@cjwyett](https://discourse.julialang.org/u/cjwyett)\
**Post date:** [December 13, 2022, 8:41pm UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/14 "2022-12-13T20:41:09Z")

</div>

> Can you demonstrate how you are using `hasfoo` in a way that makes it unsatisfactory?

```julia
f(x::MyType) = 0
f(x::MyType{Bar, Bar} = 1
f(x::MyType{Baz, Baz} = 2
f(x::MyType{Bar, Baz} = 3
f(x::MyType{Baz, Bar} = 4
f(x::MyType{Foo}) = 5
f(x::MyType{<:FooBarBaz, Foo} = 5 #!!Repeated code!!

```

Becomes

```julia
f(x::MyType{Bar, Bar} = 1
f(x::MyType{Baz, Baz} = 2
f(x::MyType{Bar, Baz} = 3
f(x::MyType{Baz, Bar} = 4
f(x::MyType) = hasfoo(x) ? 5 : 0

```

Obviously messier with actual multi-line functions, helper functions (and a lot of them!).

---

<div class="post-metadata">

**Author:** ![Jollywatt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jollywatt/32/202198_2.png) [@Jollywatt](https://discourse.julialang.org/u/Jollywatt)\
**Post date:** [December 14, 2022, 4:36am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/15 "2022-12-14T04:36:20Z")

</div>

> [@cjwyett](#):
>
> ```julia
> …
> f(x::MyType{Foo}) = 5
> f(x::MyType{<:FooBarBaz, Foo} = 5 #!!Repeated code!!
> 
> ```

If this is really the only problem, just use a union type:

```julia
const MyTypeFoo = Union{MyType{Foo},MyType{<:FooBarBaz,Foo}}

f(x::MyTypeFoo) = 5

```

(As originally suggested by @uniment!)

> [@uniment](#):
>
> For something a bit more automated, how about this?
> 
> ```julia
> julia> fooey = [:(MyType{$(fill(:(<:Any), n)...), Foo}) for n=0:4]
> 5-element Vector{Expr}:
> :(MyType{Foo})
> :(MyType{<:Any, Foo})
> :(MyType{<:Any, <:Any, Foo})
> :(MyType{<:Any, <:Any, <:Any, Foo})
> :(MyType{<:Any, <:Any, <:Any, <:Any, Foo})
> 
> julia> const FooTypes = eval(:(Union{$(fooey...)}))
> 
> ```
> 
> Then you could do
> 
> ```julia
> f(x::FooTypes) = ...
> 
> ```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 14, 2022, 5:40am UTC](https://discourse.julialang.org/t/f-x-mytype-t-s-where-t-s-foo/91581/16 "2022-12-14T05:40:24Z")

</div>

Although from this example, it looks like you can just let

```julia
f(x::MyType) = 5

```

be the default fallback to catch anything with `Foo`, as all the other variations without `Foo` are already covered 😅
