Mutiple dispatch on Tuple or "scalar"?

I want two versions of a function depending on whether the argument is a Tuple{T,T} or just T. I thought the more specific one wins, but in the code at the end of this post, the less specific x::T always wins over x::Tuple{T,T}.

My goal right now is

f(x = (3,5)) # -> calls f(x::Tuple{Int,Int})
f(x = 3)  # -> calls f(x::Int)

How does one do this?

A more general question is, how do you have two versions, one expecting a “scalar” and the other expecting a “collection” (array, range, tuple)?

function f(; x::Tuple{T,T}) where {T}
  print("tuple ")
  print(typeof(x), " ")
  @show x
function f(; x::T) where {T}
  print("single ")
  print(typeof(x), " ")
  @show x

f(x = (3,4)) # -> single Tuple{Int64, Int64} x = (3, 4)
f(x = (3,"5")) # -> single Tuple{Int64, String} x = (3, "5")
f(x = 9) # -> single Int64 x = 9

see Methods · The Julia Language. Specifically

Methods are dispatched based only on positional arguments, with keyword arguments processed after the matching method is identified.


… and see a related discussion at How can kwargs be used in multiple dispatch :slight_smile:


Although of course you can use dispatch internally to do that:

julia> _f(x::T) where {T} = 1
_f (generic function with 1 method)

julia> _f(x::Tuple{T,T}) where {T} = 2
_f (generic function with 2 methods)

julia> f(;x) = _f(x)
f (generic function with 1 method)

julia> f(x=1)

julia> f(x=(2,3))

Thank you all for your help.

Ah! That’s what I was missing!

f(;x) = _f(x)

I see!

Out of curiosity . . . why not? Why isn’t dispatch on keyword arguments allowed? (I googled it but wasn’t able to locate a discussion for the reason.)