# What is the best way to require exactly 1 of 2 keyword arguments when writing a function?

**URL:** <https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039>\
**Category:** New to Julia\
**Created:** [April 22, 2020, 7:00pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039 "2020-04-22T19:00:38Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rainbow-Anthony\_Lili](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainbow-anthony_lili/32/13299_2.png) [@Rainbow-Anthony\_Lili](https://discourse.julialang.org/u/Rainbow-Anthony_Lili)\
**Post date:** [April 22, 2020, 7:00pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/1 "2020-04-22T19:00:38Z")

</div>

I have a function “counts” that divides a sorted array of floats into subarrays based off a specified maximum value and an integer number of divisions, and then counts the length of each subarray, returning an array of lengths - the “counts” of each subsection. Currently it is something like

```julia
function counts(arr, max, divisions) ...

```

called with  
`counts(arr, 5.0, 5)`  
This function works fine for this, but I would like to overload the function to be able to express the required subsections as either an integer number of divisions, or a float interval size. This could be easily achieved by

```julia
function counts(arr, max, divisions :: Int) ...
function counts(arr, max, interval :: Float64) ...

```

However this could easily lead to misuse, where a user would get a very different result from using a float instead of an int. To avoid this, it would make sense to use keyword arguments, such that the function might be called with  
`counts(arr, 5.0, divisions = 5)`  
or  
`counts(arr, 5.0, interval = 1.0)`  
Thereby making it clearer to the user what they are doing. Exactly one of these should be required.  
Here lies the problem. I can easily make keywords required by not assigning a default value

```julia
function counts(arr, max; interval :: Float64, divisions :: Int) ...

```

but I don’t want both to be required, since either one is sufficient, and preferably I don’t want the user to try and specify both since it can only lead to mistakes. I don’t think I can do this in separate definitions as when they were not keywords, since the second just overwrites the first definition. One workaround I can imagine is to assign both keyword arguments a default value (such as 0) and then have the function check that exactly one is assigned to a non-zero value, and throw an error if not. That makes some sense since the given argument should not be 0 anyway, but manually checking that the function has been given the correct number of arguments feels wrong. Alternatively I could have an overload with one mode as a required keyword and the other not a keyword:

```julia
function counts(arr, max, divisions :: Int) ...
function counts(arr, max; interval :: Float64) ...

```

which I have a feeling some native functions may do. Is this the best approach?

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [April 22, 2020, 7:30pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/2 "2020-04-22T19:30:46Z")

</div>

Here are a couple of options:

### Option 1: Multiple keywords and a run-time check:

Just set each keyword’s default to `nothing` and verify that one of them is not `nothing` inside the body of the function. This requires slightly more typing, but it should have little or no runtime cost.

```julia-auto
julia> function counts(arr, max; interval::Union{Float64, Nothing} = nothing, divisions::Union{Float64, Nothing} = nothing)
         if interval === nothing && divisions === nothing
           error("Please provide `interval=` or `divisions=` as keyword arguments")
         end
         do_stuff()
       end
counts (generic function with 1 method)

```

### Option 2: Wrapper types

Create a wrapper type to indicate what kind of computation you are requesting, and then implement separate methods for those computations:

```julia-auto
julia> struct Divisions
         divisions::Int
       end

julia> struct Interval
         interval::Float64
       end

julia> function counts(arr, max, divisions::Divisions)
         do_stuff()
       end
counts (generic function with 4 methods)

julia> function counts(arr, max, interval::Interval)
         do_stuff()
       end
counts (generic function with 4 methods)

julia> counts([], 1, Divisions(2))

julia> counts([], 1, Interval(2))

```

This method will be easier to work with if you want to add some third method later on, since you just add more `struct` definitions and methods for `counts`, rather than having to edit an increasingly long list of keywords. This method should also impose little or no run-time cost.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [April 22, 2020, 11:18pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/3 "2020-04-22T23:18:27Z")

</div>

I think @rdeits gave good solutions. Although instead of option 2 it might just be simpler to just use two different function names something like `countsByDivisions` and `countsByInterval`. Unless there is another reason to keep the function names the same. 🙂

---

<div class="post-metadata">

**Author:** ![ettersi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ettersi/32/6829_2.png) [@ettersi](https://discourse.julialang.org/u/ettersi)\
**Post date:** [April 23, 2020, 1:09am UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/4 "2020-04-23T01:09:11Z")

</div>

**Option 3: Multiple keywords forwarded to multiple dispatch**

```julia
count(arr, max; divisions::Union{Int,Nothing} = nothing, interval::Union{Float64,Nothing} = nothing) = 
      count_impl(arr, max, divisions, interval)
count_impl(arr, max, divisions::Int, interval::Nothing) = do_stuff()
count_impl(arr, max, division::Nothing, interval::Float64) = do_stuff()

```

This is very similar to Option 1 but has zero runtime cost.

One drawback of Option 2 is that names like `Divisions` and `Interval` are likely to clash with other packages.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [April 23, 2020, 7:05am UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/5 "2020-04-23T07:05:17Z")

</div>

From the _Book of Ill-advised Dispatch Patterns in Julia_ (Miskatonic University Press, 2027, forthcoming):

```julia
f(; kwargs...) = _f((; kwargs...))
_f(kwargs::NamedTuple{(:interval,)}) = "$(kwargs.interval) interval"
_f(kwargs::NamedTuple{(:divisions,)}) = "$(kwargs.divisions) divisions"

```

In practice, I would go with Option 1 by @rdeits.

---

<div class="post-metadata">

**Author:** ![Rainbow-Anthony\_Lili](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainbow-anthony_lili/32/13299_2.png) [@Rainbow-Anthony\_Lili](https://discourse.julialang.org/u/Rainbow-Anthony_Lili)\
**Post date:** [April 23, 2020, 1:36pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/6 "2020-04-23T13:36:26Z")

</div>

Thanks for the suggestions. I had thought of option 1 but I’m trying to avoid it on the principle that runtime checks for types should be a last resort. Option 2 is an interesting thought that I hadn’t considered. It certainly does what I want, although it feels slightly more convoluted than I like. I might avoid it on this occasion, but I will keep it as a tool for potential future use.

---

<div class="post-metadata">

**Author:** ![Rainbow-Anthony\_Lili](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainbow-anthony_lili/32/13299_2.png) [@Rainbow-Anthony\_Lili](https://discourse.julialang.org/u/Rainbow-Anthony_Lili)\
**Post date:** [April 23, 2020, 1:39pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/7 "2020-04-23T13:39:42Z")

</div>

That… would make sense. In this circumstance it is as good a solution as any. It doesn’t exactly answer my question about keyword arguments but I will very likely end up doing this. I have a habit of trying to make use of overloading more than is perhaps wise…

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [April 23, 2020, 1:54pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/8 "2020-04-23T13:54:38Z")

</div>

> [@Rainbow-Anthony\_Lili](#):
>
> I’m trying to avoid it on the principle that runtime checks for types should be a last resort

FWIW, the code I posted doesn’t _actually_ cause any cost at run-time because the compiler is smart enough to figure out what will happen just from the types of the arguments. Check out this example:

```julia
julia> function foo(x)
         if x === nothing
           return "hello world"
         else
           return 1.0
         end
       end
foo (generic function with 1 method)

```

This looks like it’s type-unstable because it could either return a `String` or a `Float64` depending on the value of `x`. But the compiler is smart enough to know that the _only_ case for which `x === nothing` holds is if `x` is of type `Nothing`. So it can actually figure out which branch of the `if` statement will be taken just from the type of the argument, and no checks have to happen at run-time. For example:

```julia
julia> @code_warntype foo(nothing)
Variables
  #self#::Core.Compiler.Const(foo, false)
  x::Core.Compiler.Const(nothing, false)

Body::String
1 ─ %1 = (x === Main.nothing)::Core.Compiler.Const(true, false)
│ %1
└── return "hello world"
2 ─ Core.Compiler.Const(:(return 1.0), false)

```

```julia
julia> @code_warntype foo(2)
Variables
  #self#::Core.Compiler.Const(foo, false)
  x::Int64

Body::Float64
1 ─ %1 = (x === Main.nothing)::Core.Compiler.Const(false, false)
└── goto #3 if not %1
2 ─ Core.Compiler.Const(:(return "hello world"), false)
3 ┄ return 1.0

```

The `if` statement has been removed at compile time, and there’s no actual type instability.

This is a pretty convenient pattern in Julia: just write out the code in a straightforward obvious way, including using run-time checks if that’s convenient. You can use `@code_warntype` to check for type instabilities and `@btime` (from BenchmarkTools) to check for performance problems and only go to more complicated type manipulation if it’s actually necessary.

---

<div class="post-metadata">

**Author:** ![Rainbow-Anthony\_Lili](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainbow-anthony_lili/32/13299_2.png) [@Rainbow-Anthony\_Lili](https://discourse.julialang.org/u/Rainbow-Anthony_Lili)\
**Post date:** [April 23, 2020, 1:58pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/9 "2020-04-23T13:58:43Z")

</div>

That’s an interesting workaround. I think it’s probably the closest answer to what I wanted. Am I correct in thinking that `count(arr, max)` will still throw an error at runtime, but here explicitly using the internal type checking rather than a manual error branch?

---

<div class="post-metadata">

**Author:** ![Rainbow-Anthony\_Lili](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainbow-anthony_lili/32/13299_2.png) [@Rainbow-Anthony\_Lili](https://discourse.julialang.org/u/Rainbow-Anthony_Lili)\
**Post date:** [April 23, 2020, 2:00pm UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/10 "2020-04-23T14:00:59Z")

</div>

Wow that’s impressive work from the compiler! I haven’t delved into those meta tools yet, I will have to give them a try.

---

<div class="post-metadata">

**Author:** ![ettersi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ettersi/32/6829_2.png) [@ettersi](https://discourse.julialang.org/u/ettersi)\
**Post date:** [April 24, 2020, 1:09am UTC](https://discourse.julialang.org/t/what-is-the-best-way-to-require-exactly-1-of-2-keyword-arguments-when-writing-a-function/38039/11 "2020-04-24T01:09:15Z")

</div>

> Am I correct in thinking that `count(arr, max)` will still throw an error at runtime, but here explicitly using the internal type checking rather than a manual error branch?

Yes. Except that one could argue that the error occurs at compile time rather than runtime, but the difference isn’t all that relevant in Julia.

Here’s a concrete example:

```julia
julia> foo(;a::Union{Int,Nothing} = nothing, b::Union{Int,Nothing} = nothing) = foo_impl(a,b)
       foo_impl(a::Int, b::Nothing) = println("foo(a = $a)")
       foo_impl(a::Nothing, b::Int) = println("foo(b = $b)")

julia> foo()
ERROR: MethodError: no method matching foo_impl(::Nothing, ::Nothing)

julia> foo(a = 42)
foo(a = 42)

julia> foo(b = 42)
foo(b = 42)

julia> foo(a = 42, b = 42)
ERROR: MethodError: no method matching foo_impl(::Int64, ::Int64)

```

As you can see, you indeed get an error for argument combinations that you did not define, but the error message might be a bit confusing for a user. But you can easily fix the error message by adding more methods to `foo_impl()`.
