Skip to content

Commit 7040839

Browse files
committed
implement #6614, destructuring in formal arguments
1 parent 7f1623e commit 7040839

File tree

4 files changed

+78
-0
lines changed

4 files changed

+78
-0
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ New language features
77
* Local variables can be tested for being defined
88
using the new `@isdefined variable` macro ([#22281]).
99

10+
* Destructuring in function arguments: when an expression such as `(x, y)` is used as
11+
a function argument name, the argument is unpacked into local variables `x` and `y`
12+
as in the assignment `(x, y) = arg` ([#6614]).
13+
1014
Language changes
1115
----------------
1216

doc/src/manual/functions.md

+19
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,25 @@ end
267267

268268
This has the exact same effect as the previous definition of `foo`.
269269

270+
## Argument destructuring
271+
272+
The destructuring feature can also be used within a function argument.
273+
If a function argument name is written as a tuple (e.g. `(x, y)`) instead of just
274+
a symbol, then an assignment `(x, y) = argument` will be inserted for you:
275+
276+
```julia
277+
julia> minmax(x, y) = (y < x) ? (y, x) : (x, y)
278+
279+
julia> range((min, max)) = max - min
280+
281+
julia> range(minmax(10, 2))
282+
8
283+
```
284+
285+
Notice the extra set of parentheses in the definition of `range`.
286+
Without those, `range` would be a two-argument function, and this example would
287+
not work.
288+
270289
## Varargs Functions
271290

272291
It is often convenient to be able to write functions taking an arbitrary number of arguments.

src/julia-syntax.scm

+29
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,32 @@
993993
(loop (cadr ex) (append! (reverse (cddr ex)) vars))
994994
`(where ,ex ,.(reverse! vars)))))
995995

996+
(define (lower-destructuring-args argl)
997+
(define (check-lhs a)
998+
(if (expr-contains-p (lambda (e) (or (decl? e) (assignment? e) (kwarg? e)))
999+
a)
1000+
(error (string "invalid argument destructuring syntax \"" (deparse a) "\""))
1001+
a))
1002+
(define (transform-arg a)
1003+
(cond ((and (pair? a) (eq? (car a) 'tuple))
1004+
(let ((a2 (gensy)))
1005+
(cons a2 `(local (= ,(check-lhs a) ,a2)))))
1006+
((or (and (decl? a) (length= a 3)) (kwarg? a))
1007+
(let ((x (transform-arg (cadr a))))
1008+
(cons `(,(car a) ,(car x) ,(caddr a)) (cdr x))))
1009+
((vararg? a)
1010+
(let ((x (transform-arg (cadr a))))
1011+
(cons `(... ,(car x)) (cdr x))))
1012+
(else (cons a #f))))
1013+
(let loop ((argl argl)
1014+
(newa '())
1015+
(stmts '()))
1016+
(if (null? argl)
1017+
(cons (reverse newa) (reverse stmts))
1018+
(let ((a (transform-arg (car argl))))
1019+
(loop (cdr argl) (cons (car a) newa)
1020+
(if (cdr a) (cons (cdr a) stmts) stmts))))))
1021+
9961022
(define (expand-function-def- e)
9971023
(let* ((name (cadr e))
9981024
(where (if (and (pair? name) (eq? (car name) 'where))
@@ -1040,6 +1066,9 @@
10401066
(farg (if (decl? name)
10411067
(adj-decl name)
10421068
`(|::| |#self#| (call (core Typeof) ,name))))
1069+
(argl-stmts (lower-destructuring-args argl))
1070+
(argl (car argl-stmts))
1071+
(body (insert-after-meta body (cdr argl-stmts)))
10431072
(argl (fix-arglist
10441073
(arglist-unshift argl farg)
10451074
(and (not (any kwarg? argl)) (not (and (pair? argl)

test/core.jl

+26
Original file line numberDiff line numberDiff line change
@@ -5508,3 +5508,29 @@ for U in unboxedunions
55085508
end
55095509

55105510
end # module UnionOptimizations
5511+
5512+
# issue #6614, argument destructuring
5513+
f6614((x, y)) = [x, y]
5514+
@test f6614((4, 3)) == [4, 3]
5515+
g6614((x, y), (z,), (a, b)) = (x,y,z,a,b)
5516+
@test g6614((1, 2), (3,), (4, 5)) === (1,2,3,4,5)
5517+
@test_throws MethodError g6614(1, 2)
5518+
@test_throws MethodError g6614((1, 2), (3,))
5519+
@test_throws BoundsError g6614((1, 2), (3,), (1,))
5520+
h6614((x, y) = (5, 6)) = (y, x)
5521+
@test h6614() == (6, 5)
5522+
@test h6614((4, 5)) == (5, 4)
5523+
ff6614((x, y)::Tuple{Int, String}) = (x, y)
5524+
@test ff6614((1, "")) == (1, "")
5525+
@test_throws MethodError ff6614((1, 1))
5526+
gg6614((x, y)::Tuple{Int, String} = (2, " ")) = (x, y)
5527+
@test gg6614() == (2, " ")
5528+
function hh6614()
5529+
x, y = 1, 2
5530+
function g((x,y))
5531+
# make sure x and y are local
5532+
end
5533+
g((4,5))
5534+
x, y
5535+
end
5536+
@test hh6614() == (1, 2)

0 commit comments

Comments
 (0)