Julia: Make semicolon mandatory in kw args calls?

Created on 25 Feb 2018  ·  7Comments  ·  Source: JuliaLang/julia

I have a function that takes some positional parameters and an args Dict/Vector{Pair}:

julia> f(a, b, c, args) = @show(a, b, c, args)

... and a wrapper to allow passing the args Pairs using kw syntax:

julia> f(a...; b...) = f(a..., b)

This works nicely...

julia> f(1, 2, 3, foo=1, bar=2)
a = 1
b = 2
c = 3
args = Any[(:foo, 1), (:bar, 2)]

julia> f(1, 2, 3, [:foo=>1, :bar=>2])
a = 1
b = 2
c = 3
args = Pair{Symbol,Int64}[:foo=>1, :bar=>2]

julia> f(1, 2, 3, foo=[:a=>1, :b=>2])
a = 1
b = 2
c = 3
args = Any[(:foo, Pair{Symbol,Int64}[:a=>1, :b=>2])]

julia> f(1, 2, 3; foo=[:a=>1, :b=>2], :bar=>7)
a = 1
b = 2
c = 3
args = Any[(:foo, Pair{Symbol,Int64}[:a=>1, :b=>2]), (:bar, 7)]

But in this case, an infinite loop occurs:

julia> f(1, 2, 3, foo=[:a=>1, :b=>2], :bar=>7)
💣

The only difference between this and the previous example is the ; after the 3.

I can't think of another example off the top of my head, but I think I've been burned by hard to understand behaviour before where putting a semicolon before the kw args was the answer.

Why not make the ';' mandatory?

Another variation of this problem is like this (filling in structure with string keys, and accidentally making the last kw arg a string): https://github.com/JuliaCloud/AWSCore.jl/issues/28

julia> f(1, 2, 3, foo=["a"=>1, "b"=>2], "bar"=>7)
💣 infinite loop

With the semicolon, this example produces a useful-ish error message:

f(1, 2, 3; foo=["a"=>1, "b"=>2], "bar"=>7)
ERROR: TypeError: anonymous: in typeassert, expected Symbol, got String

Most helpful comment

I find it incredibly useful that we allow mixing positional and keyword arguments and make use of it in my Hyperscript package for dealing with DOM trees:

div(class="entry", h1("An Important Announcement"))
m("div", class="entry",
    m("h1", "An Important Announcement"))

All 7 comments

I think this would be way too fussy, It would be a shame to lose elegant syntax like f(a=1, b=2).

I was surprised to see that we allow positional arguments following a keyword one. Removing that would be another path towards alleviating the same problem.

I find it incredibly useful that we allow mixing positional and keyword arguments and make use of it in my Hyperscript package for dealing with DOM trees:

div(class="entry", h1("An Important Announcement"))
m("div", class="entry",
    m("h1", "An Important Announcement"))

Making it mandatory seems unnecessary. In your own code you can use the semicolon and put keyword args after positional args to avoid potential issues.

In your own code you can use the semicolon and put keyword args after positional args to avoid potential issues.

I have gotten into the habit of doing this.

I wasn't aware that mixing positional and keyword arguments was allowed. Is this new?

The reason for posting this issue was that it has been confusing for users of my packages. I guess the answer is that I'll have to be more careful to create APIs that avoid the issue and find another way to implement wrapper methods like f(a...; kw...) = f(a..., kw). I suspect that if I reviewed the relevant APIs with an eye to taking advantage of the latest kw args improvements the issue might disappear anyway.

I wasn't aware that mixing positional and keyword arguments was allowed. Is this new?

Nope, it's been that way from the beginning.

I've been sort of annoyed by this on several occasions, but for a slightly different reason.

Often I want to be able to have keywords with ! in them. It's been requested for example to have OnceDifferentiable(f = nothing, f! = nothing, g = nothing, g! = nothing) . But this is not good, because then I will have users complaining that the constructor is broken all the time (happened when we had a linesearch! keyword). Why? Because they enter

myobjective = OnceDifferentiable(f!=mymutatingobjective)

Why does this fail? It fails because != is parsed as no-equal. Of course they should write

myobjective = OnceDifferentiable(f! = mymutatingobjective)

buy space-sensitive APIs are something I want to avoid. This means that the keyword has to be something like OnceDifferentiable(mutating_f = ...) or similar, which is equally horrible.

A case where ; was required could possibly enable more intelligent parsing that could recognize that f! was indeed a keyword name and before a = so it does not mean f neq ....

Anyway, I don't expect this to be changed so close to v1.0...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TotalVerb picture TotalVerb  ·  3Comments

i-apellaniz picture i-apellaniz  ·  3Comments

m-j-w picture m-j-w  ·  3Comments

StefanKarpinski picture StefanKarpinski  ·  3Comments

StefanKarpinski picture StefanKarpinski  ·  3Comments