Julia: Printing of kw-args with bang needs a space

Created on 5 Dec 2019  路  16Comments  路  Source: JuliaLang/julia

Keyword arguments are usually written without space: fn(;a=1) = a^2. However if the kwarg ends in a bang this does not work:

julia> f(;fn!=x->x[1]=1) = 1
ERROR: syntax: invalid keyword argument syntax "(fn != x -> begin
    # REPL[24], line 1
    x[1] = 1
end)"
Stacktrace:
 [1] top-level scope at REPL[24]:1

and a space is needed. Could no-space be supported?

This would then also be consistent with the current printing of the Expr:

julia> Expr(:kw, :fn!, :a)
:(fn!=a)
display and printing parser

All 16 comments

No, I really think it is too confusing to disambiguate fn!=x differently in different contexts. I would say the expression printing is a bug here, since it won't be parsed back correctly.

Fair enough. I changed the title accordingly.

TIL this works

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

julia> f()
2

We could just paren-wrap keyword arguments that end with a bang.

I think the best thing to do is just put spaces around the = like we do for assignment expressions.

One of the nice things about omitting spaces around = for keyword arguments is that it disambiguates them from assignments. I would personally consider that a regression.

It doesn't disambiguate them to the parser though.

In fact, the more comprehensive fix here would be to print kw expressions outside a call as Expr(:kw, ...) and = expressions inside a call as Expr(:(=), ...).

That makes sense generally speaking, but printing as Expr(:(=), ) loses the visual disambiguation for the user from actual assignments though, since Expr(:(=), :a, :b) prints as :(a = b), so you'd still end up with confusing-looking printing inside of call expressions. That's why I suggest wrapping with parentheses if endswith(a, '!') as part of printing of :kw expressions.

Or using var"a!".

IMO the value of printing expressions so that they can be parsed back accurately is worth slightly uglier output in some corner cases.

I agree with that in general, but printing as Expr(:(=), ) produces uglier output in _all_ cases for the benefit of corner cases.

No, it's that f(a = 1) parses as Expr(:call, :f, Expr(:kw, :a, 1)). So if you actually have Expr(:call, :f, Expr(:(=), :a, 1)) it should be printed as f($(Expr(:(=), :a, 1)). And the reverse outside a call argument list.

Okay, I think that makes more sense. So with your proposed change, we'd have

julia> print(:(f(a=1)))
f($(Expr(:(=), :a, 1))

?

No, that would print as :(f(a = 1)).

What about print(:(f; a=1))?

The goal is for print(:(XXX)) to always print something as close to :(XXX) as possible. There you've already entered valid syntax by construction, since the expression object was formed by the parser. The weirder cases are expressions manually made with Expr that the parser never forms. Expr(:call, :f, Expr(:(=), :a, 1)) is one of those, since the parser uses kw for = inside calls.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manor picture manor  路  3Comments

i-apellaniz picture i-apellaniz  路  3Comments

m-j-w picture m-j-w  路  3Comments

tkoolen picture tkoolen  路  3Comments

Keno picture Keno  路  3Comments