Keyword argument types - what's going on?

Hey everyone,

I’m new-ish to Julia and have been loving it so far, but I just stumbled on some behavior I’m confused about when it comes to types and keyword arguments.

As per the docs, I can define a function accepting e.g. Int vs Float64 to create two methods which behave differently

f(x::Int64) = x
f(x::Float64) = 2x
# f (generic function with 2 methods)

f(1) # == 1
f(1.0) # == 2.0

However, if I want to do the same for a keyword argument:

g(x; y::Int64) = x + y
g(x; y::Float64) = x * y
# g (generic function with 1 method)

it doesn’t create two methods that behave differently based on the type of y. What’s more, this throws an error:

g(2; y=1) # ERROR: TypeError: in keyword argument y, expected Float64, got a value of type Int64
g(2; y=1.0) # == 2.0

The same happens if I assign default values to the keyword argument:

function h(x;y::Int=1)
    ###
end


function h(x; y::Float64=1.0)
    ###
end
# h (generic function with 1 method)

Now, I have 2 questions:

  1. why does this happen? I’m guessing it’s because both Int and Float64 are subtypes of the same abstract type, but it works fine when they’re not keyword arguments.
  2. How can I work around this, if I want two methods that behave differently based on the type of y?

Specifically, in my case I’m writing a function that creates some pseudo random events and users can either pass a p::Float64 probability value or an n::Int number of events to be produced. The logic of the function is very different based on the type of the parameter, and I want them to be keyword arguments. I could put an if statement inside the function, but I thought multiple dispatch should be able to handle this for me!

Any help would be greatly appreciated, thank you!

1 Like

It is not possible to dispatch on keyword arguments with base Julia. You might be interested in this package: https://github.com/simonbyrne/KeywordDispatch.jl

1 Like

Hi @FedeClaudi ,

no. The type dispatch tries to always resolve to the most concrete type. If that’s not possible like here (neither is Int64 more concrete than Float64, nor is the reverse true) then it would error (if those were positional arguments).

Julia explicitely does not dispatch on keyword arguments as mentiond here in the doc:
https://docs.julialang.org/en/v1/manual/methods/#Note-on-Optional-and-keyword-Arguments

Keyword arguments behave quite differently from ordinary positional arguments. In particular, they do not participate in method dispatch.
1 Like

Then I think those two inputs should have different keyword names. If the keyword isn’t descriptive, you might as well make it a regular positional argument.

Ooooh I missed that @trahflow, thank you!
It looks like https://github.com/simonbyrne/KeywordDispatch.jl should do the trick, thank you @Christopher_Fisher !

2 Likes

I suspect that’s going about it the wrong way.

They already do?

Blockquote I suspect that’s going about it the wrong way.

Why do you think that? Is it not Julian? Performance costs?
The only other solution I can think of is having both n, p as keyword arguments to a single methods that then behaves differently based on which one is used, but I felt that it wouldn’t be the most Julian way to do this, any suggestion on how do it better?

Do they, I thought you just used the name y for both?

Yes, that is what I was suggesting, though I would suggest something slightly more descriptive.

In the minimal example above I’ve used the same y name, in my code I have n, p.

I would suggest something slightly more descriptive.

:+1:

Ah, ok. But I thought you wanted the same keyword name to have different behaviour depending on argument type. I guess I don’t understand how the problem arose in the first place then. Why did you need dispatch at all then?

Yes I think t it’s better to have one method defined as e.g. f(x; n=nothing, p=nothing) and in the body of f check which argument is given by the user. In this body you can then dispatch the implementation to two different helper functions.

2 Likes

Sorry I guess how I phrased the question might have been a bit confusing.

I need:

f(...; n::Int)
f(...; p::Float64)

to be different methods. However, since keyword types doesn’t affect dispatch, the scond definition with Float overwrites the first one and calling f(...; n=1) gives an argument error because n is not in the function signature anymore.

In the example I’ve started with I’ve tried to go to the root at the problem which is that the type of the keyword argument was being ignored when defining multiple methods, and now I know that this is the case (although it’s not clear to me the reason for this in Julia).

Thanks @sijo

2 Likes