# Julian Way of Using Keyword Arguments

**URL:** <https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777>\
**Category:** General Usage\
**Tags:** question\
**Created:** [May 19, 2020, 8:02pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777 "2020-05-19T20:02:02Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [May 19, 2020, 8:02pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/1 "2020-05-19T20:02:03Z")

</div>

I am implementing a model for the units of a simple silica glass, and in doing so have come across some interesting questions on how to use keyword arguments. Essentially, there are two notations for how to represent the composition of the glass (the input to my function): x, and j.

In order to represent that, here is what I’ve done for one such unit

```julia
x(j) = j / (j + 1)
Q4(; j = 0, x = x(j)) = 0 <= x < 1 / 3 ? 1 - 3 * x : 0

```

It strikes me as odd that with this particular use case, I must specify a default value for j, even if it’s never used except for when a j-value is specified and x is not. Also, it’s possible, though it would error, to attempt to call this function without either j or x. This hints to me that I’m doing something “wrong,” or at least in a not very Julian/idiomatic fashion.

Additionally, when I went to plot this, I found that dot notation for vectorization doesn’t work with keyword arguments (noted in a few places a few years ago, including here: [Julia: Broadcasting Functions with Keyword Arguments - Stack Overflow](https://stackoverflow.com/questions/48738016/julia-broadcasting-functions-with-keyword-arguments)). Instead, I did something like this:

```julia
using Plots
xrange = 0:1/1000:2/3
plot(xrange, map(x -> Q4(x = x), xrange))

```

It gets a little unwieldy, but not terrible, when plotting something like the fraction of units

```julia
total(; j = 0, x = x(j)) = Q4(x = x) + Q3(x = x) + Q2(x = x) + Q1(x = x) + Q0(x = x)
plot(xrange, map(x -> Q4(x = x) / total(x = x), xrange))

```

Is this the preferred way to handle vectorization of functions with keyword arguments, or is there a better way?

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [May 19, 2020, 8:14pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/2 "2020-05-19T20:14:05Z")

</div>

A few thoughts

1. Consider making the arguments positional arguments rather than keyword arguments. You can use a docstring to tell the reader what the arguments mean. This also means you can change the internal API easier.
2. Re-factor your code so it only takes one input. It’s not obvious why your `Q4` function needs two arguments if `x` is fully determined by `j`
3. Use structs. If you find yourself passing a lot of information around which uses the same parameters over and over again, consider putting everything you need for your model in a `struct` and passing that around instead.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [May 19, 2020, 11:11pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/3 "2020-05-19T23:11:14Z")

</div>

Thanks! I ended implementing most of thoose suggestions

My type system ended up looking like this:

```julia
import Base: promote_rule, -
struct j <: AbstractFloat
    value::Float64
end

struct x <: AbstractFloat
    value::Float64
end

# Conversions
j(x::x) = j(x.value / (1 - x.value))
x(j::j) = x(j.value / (j.value + 1))
x(x::x) = x
Float64(x::x) = x.value
Float64(j::j) = j.value

# Operators
-(j1::j, j2::j) = j(j1.value - j2.value)
-(x1::x, x2::x) = x(x1.value - x2.value)

# Promotion rules
promote_rule(::Type{Int64}, ::Type{j}) = Float64
promote_rule(::Type{Float64}, ::Type{j}) = Float64
promote_rule(::Type{Int64}, ::Type{x}) = Float64
promote_rule(::Type{Float64}, ::Type{x}) = Float64;

```

I’m not particularly happy with that third line under conversions, but it allowed me to write and plot the model like this

```julia
Q4(v) = 0 <= x(v) < 1 / 3 ? 1 - 3 * x(v) : 0
range = x.(0:1/1000:2/3)
plot(range, Q4.(range))

```

I think I could probably fix that with typing the Q4 function and using multiple dispatch, but it’s already looking much better in my opinion.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [May 20, 2020, 1:20pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/4 "2020-05-20T13:20:10Z")

</div>

This is an interesting solution, but I think it’s overkill for what you are trying to accomplish.

The method I mentioned above was really about using structs as storage, not overloading behavior for computation. Say you have a model with parameters `k` and `z`, then it’s useful to make a struct

```julia
struct MyModel{T, S} where {T<:Real, S<:Real}
    k::T
    z::S
end

```

Then when you need to work with your model you have

```julia
function calculate(x<:Real, y <:Real)
   x + y # computation on reals
end

function work_with_model(m::MyModel)
    return m.k + calculate(m.k, m.z)
end

```

Importantly, `MyModel` is not a number-like thing. It’s just a convenient way to store numbers. It’s really just a `NamedTuple` where I enforce what fields and types it has. This is a totally valid use of structs.

In your example, you are treating `j` and `x` as number-like things, which I would bet is unnecessary. There are certainly reasons to define your own object that behaves like a number, like complex numbers, where you want to define your _own rules_ for how algebra works with complex numbers.

But you aren’t doing anything special with your overloading. `+` and `-` work just the same as with Floats, so you might as well operate with the values themselves.

Additionally, I think you are confusing functions with structs. With this line

```julia
Q4(v) = 0 <= x(v) < 1 / 3 ? 1 - 3 * x(v) : 0

```

As far as I can tell, `x(v)` doesn’t do anything special, right? No calculation is performed on the constructor `x(v)` and `x` objects behave just like floats. Why not just use the value of `v`?

I encourage you to keep playing around with Julia’s type system, but I would not that this isn’t a very idiomatic way of doing things. My general advice is

1. Use structs for storage of parameters to enforce code consistency and make it easier to type.
2. If you _really_ need a number-like thing that has it’s own behavior that could not be accomplished just by working with the values themselves, then define all the number-like methods you need and conversion methods. But know that is a lot of work (there are a lot of methods) and adding conversions can make code very confusing to read, as it’s not obvious when the a value is a float and when it isn’t.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [May 20, 2020, 2:27pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/5 "2020-05-20T14:27:59Z")

</div>

> [@pdeffebach](#):
>
> As far as I can tell, `x(v)` doesn’t do anything special, right? No calculation is performed on the constructor `x(v)` and `x` objects behave just like floats. Why not just use the value of `v` ?

`x(v)` makes sure that the composition is reported using the x notation (if it’s using j, it will convert, if it’s x, it will return the same thing. Additionally, if you wanted to, you could pass in a Float64 and that would work as well (because this would construct an x with the value of v).

Yeah, I learned a good deal about the type system as I was writing this, but yeah, a more idiomatic way to do this would be using structs in a different place, as you suggested.

---

<div class="post-metadata">

**Author:** ![Ian\_Slagle](https://avatars.discourse-cdn.com/v4/letter/i/b5a626/32.png) [@Ian\_Slagle](https://discourse.julialang.org/u/Ian_Slagle)\
**Post date:** [May 20, 2020, 3:01pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/6 "2020-05-20T15:01:59Z")

</div>

Would a more idiomatic way of using structs here be something like this?

```julia
j_to_x(j) = j / (j + 1)
x_to_j(x) = x / (1 - x)

struct Glass
    Q4::Float64
    Q3::Float64
    Q2::Float64
    Q1::Float64
    Q0::Float64
end

function Glass(c; notation = "x")
    if notation == "j"
        glass = Glass(j_to_x(c))
    else
        Q4 = 0 <= c < 1 / 3 ? 1 - 3 * c : 0
        Q3 = 0 < c <= 1 / 3 ? 2 * c : 1 / 3 < c < 1 / 2 ? 2 - 4 * c : 0
        Q2 = 1 / 3 < c <= 1 / 2 ? 3 * c - 1 : 1 / 2 < c < 3 / 5 ? 3 - 5 * c : 0
        Q1 = 1 / 2 < c <= 3 / 5 ? 4 * c - 2 : 3 / 5 < c < 2 / 3 ? 4 - 6 * c : 0
        Q0 = 3 / 5 < c < 2 / 3 ? 5 * c - 3 : 0
        glass = Glass(Q4, Q3, Q2, Q1, Q0)
    end
    return glass
end   

total(g::Glass) = g.Q4 + g.Q3 + g.Q2 + g.Q1 + g.Q0
BO(g::Glass) = (4 * g.Q4 + 3 * g.Q3 + 2 * g.Q2 + g.Q1) / total(g)
NBO(g::Glass) = 4 - BO(g);

```

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [May 20, 2020, 3:20pm UTC](https://discourse.julialang.org/t/julian-way-of-using-keyword-arguments/39777/7 "2020-05-20T15:20:29Z")

</div>

Yeah this seems prettio idiomatic. But I don’t know the details of glass simulation or really have much experience with these types of problems. Hopefully someone else can chime in here.

You might be interest in the `@unpack` macro from [Parameters.jl](https://github.com/mauro3/Parameters.jl) which will make your life a bit easier.
