Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add implicit named tuple and keyword argument names #34331

Merged
merged 2 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ New language features
macros and matrix constructors, which are whitespace sensitive, because expressions like
`[a ±b]` now get parsed as `[a ±(b)]` instead of `[±(a, b)]`. ([#34200])

* Passing an identifier `x` by itself as a keyword argument or named tuple element
is equivalent to `x=x`, implicitly using the name of the variable as the keyword
or named tuple field name.
Similarly, passing an `a.b` expression uses `b` as the keyword or field name ([#29333]).

* Packages can now provide custom hints to help users resolve errors by using the
`register_error_hint` function. Packages that define custom exception types
can support hints by calling `show_error_hints` from their `showerror` method. ([#35094])
Expand Down
16 changes: 16 additions & 0 deletions base/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ julia> keys = (:a, :b, :c); values = (1, 2, 3);
julia> (; zip(keys, values)...)
(a = 1, b = 2, c = 3)
```

As in keyword arguments, identifiers and dot expressions imply names:

```jldoctest
julia> x = 0
0

julia> t = (; x)
(x = 0,)

julia> (; t.x)
(x = 0,)
```

!!! compat "Julia 1.5"
Implicit names from identifiers and dot expressions are available as of Julia 1.5.
"""
Core.NamedTuple

Expand Down
4 changes: 4 additions & 0 deletions doc/src/manual/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,10 @@ One can also pass `key => value` expressions after a semicolon. For example, `pl
is equivalent to `plot(x, y, width=2)`. This is useful in situations where the keyword name is computed
at runtime.

When a bare identifier or dot expression occurs after a semicolon, the keyword argument name is
implied by the identifier or field name. For example `plot(x, y; width)` is equivalent to
`plot(x, y; width=width)` and `plot(x, y; options.width)` is equivalent to `plot(x, y; width=options.width)`.

The nature of keyword arguments makes it possible to specify the same argument more than once.
For example, in the call `plot(x, y; options..., width=2)` it is possible that the `options` structure
also contains a value for `width`. In such a case the rightmost occurrence takes precedence; in
Expand Down
4 changes: 4 additions & 0 deletions src/ast.scm
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@
(cadr e)
e))

(define (quoted-sym? e)
(and (length= e 2) (memq (car e) '(quote inert))
(symbol? (cadr e))))

(define (lam:args x) (cadr x))
(define (lam:vars x) (llist-vars (lam:args x)))
(define (lam:vinfo x) (caddr x))
Expand Down
9 changes: 4 additions & 5 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1758,10 +1758,10 @@
(syntax-str "named tuple element"))
(let* ((names (apply append
(map (lambda (x)
(cond #;((symbol? x) (list x))
(cond ((symbol? x) (list x))
((and (or (assignment? x) (kwarg? x)) (symbol? (cadr x)))
(list (cadr x)))
#;((and (length= x 3) (eq? (car x) '|.|))
((and (length= x 3) (eq? (car x) '|.|))
(list (cadr (caddr x))))
(else '())))
lst)))
Expand Down Expand Up @@ -1799,18 +1799,17 @@
(cons (cadr el) current-names)
(cons (caddr el) current-vals)
expr))
#|
((symbol? el) ;; x => x = x
(loop (cdr L)
(cons el current-names)
(cons el current-vals)
expr))
((and (length= el 3) (eq? (car el) '|.|)) ;; a.x => x = a.x
((and (length= el 3) (eq? (car el) '|.|) ;; a.x => x = a.x
(quoted-sym? (caddr el)))
(loop (cdr L)
(cons (cadr (caddr el)) current-names)
(cons el current-vals)
expr))
|#
((and (length= el 4) (eq? (car el) 'call) (eq? (cadr el) '=>))
(loop (cdr L)
'()
Expand Down
6 changes: 5 additions & 1 deletion src/macroexpand.scm
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,11 @@
((parameters)
(cons 'parameters
(map (lambda (x)
(resolve-expansion-vars- x env m parent-scope #f))
;; `x` by itself after ; means `x=x`
(let ((x (if (and (not inarg) (symbol? x))
`(kw ,x ,x)
x)))
(resolve-expansion-vars- x env m parent-scope #f)))
(cdr e))))

((= function)
Expand Down
5 changes: 5 additions & 0 deletions test/keywordargs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ kwf1(ones; tens=0, hundreds=0) = ones + 10*tens + 100*hundreds
@test kwf1(2, tens=6) == 62
@test kwf1(1, hundreds=2, tens=7) == 271
@test kwf1(3, tens=7, hundreds=2) == 273
let tens = 2, hundreds = 4
@test kwf1(8; tens, hundreds) == 428
nt = (hundreds = 5,)
@test kwf1(7; nt.hundreds) == 507
end

@test_throws MethodError kwf1() # no method, too few args
@test_throws MethodError kwf1(1, z=0) # unsupported keyword
Expand Down
11 changes: 11 additions & 0 deletions test/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,14 @@ end
@test_throws LoadError include_string(Main, "@NamedTuple{a::Int, b, 3}")
@test_throws LoadError include_string(Main, "@NamedTuple(a::Int, b)")
end

# issue #29333, implicit names
let x = 1, y = 2
@test (;y) === (y = 2,)
a = (; x, y)
@test a === (x=1, y=2)
@test (; a.y, a.x) === (y=2, x=1)
y = 3
@test Meta.lower(Main, Meta.parse("(; a.y, y)")) == Expr(:error, "field name \"y\" repeated in named tuple")
@test (; a.y, x) === (y=2, x=1)
end
3 changes: 3 additions & 0 deletions test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,9 @@ end
@test i0xb23hG() == 2
@test i0xb23hG(x=10) == 10

accepts__kwarg(;z1) = z1
@test (@id_for_kwarg let z1 = 41; accepts__kwarg(; z1); end) == 41

@test @eval let
(z,)->begin
$(Expr(:inbounds, true))
Expand Down