Repository: hylang/hy

Issue:
(feature request) function argument destructuring
In clojure, I can do

``` clojure
(defn awe [[x y]] (+ x y))
(awe [2 3]) ; => 5
```

But not in hy :( destructuring within a `let` works just fine though.

Why is this useful? A larger example

``` clojure
(map
  (fn [[x y]] (, (int x) y)))
  (.items my-dictionary))
```

Which otherwise needs to be one of

``` clojure
(map
  (fn [args] (let [[[x y] args]] (, (int x) y))))
  (.items my-dictionary))
```

or

``` clojure
(map
  (fn [args] (, (int (. args [0]) (. args[1]))))
  (.items my-dictionary))
```



Discussion:
This is pretty simple to implement yourself:

``` hy
(import [hy.models.symbol [HySymbol]] [hy.models.list [HyList]])

(defmacro fnd-base [inv ext args body]
  (setv skipx false)
  (setv argr [])
  (for [arg args]
    (argr.append arg)
    (when skipx
      (setv skipx false)
      (continue))
    (when (isinstance arg HySymbol)
      (setv skipx true)
      (continue))
    (when (isinstance arg HyList)
      (setv oldarg arg)
      (setv arg (gensym))
      (assoc argr (dec (len argr)) arg)
      (setv body (+ [`(setv ~oldarg ~arg)] body))))
  `(~inv ~@ext [~@argr] ~@body))

(defmacro fnd [args &rest body]
  `(fnd-base fn [] [~@args] [~@body]))

(defmacro defnd [name args &rest body]
  `(fnd-base defn [~name] [~@args] [~@body]))
```

Pop it into a file named `fnd.hy` and use it like so:

``` hy
(require fnd)

; fnd instead of fn
((fnd [[x y]] (print x y)) [1 2])

; defnd instead of defn
(defnd myfunc [[x y]]
  (print x y))

(myfunc [1 2])
```

but why not have it be a language feature? Python already has function argument destructuring built-in

That's Python 2 only - and I think we can make it work by fixing the
argument compiles --

with hy --spy:

```
=> (defn x [(, a b)])
def x((u',' u'a' u'b')):
    pass
=>
```

appears to be buggy

On Tue, Apr 7, 2015 at 3:23 PM, Jared Forsyth notifications@github.com
wrote:

> but why not have it be a language feature? Python already has function
> argument destructuring built-in
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/hylang/hy/issues/792#issuecomment-90705742.

## 

:wq


Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -1934,6 +1934,19 @@ def compile_function_def(self, expression):
             raise HyTypeError(expression,
                               "First argument to (fn) must be a list")
         ret, args, defaults, stararg, kwargs = self._parse_lambda_list(arglist)
+        for i, arg in enumerate(args):
+            if isinstance(arg, HyList):
+                # Destructuring argument
+                if not arg:
+                    raise HyTypeError(arglist,
+                                      "Cannot destruct empty list")
+                args[i] = var = HySymbol(self.get_anon_var())
+                expression = HyExpression([
+                    HyExpression([
+                        HyString("setv"), arg, var
+                    ])]
+                ) + expression
+                expression = expression.replace(arg[0])
 
         if PY34:
             # Python 3.4+ requires that args are an ast.arg object, rather


Test patch:
diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py
--- a/tests/compilers/test_ast.py
+++ b/tests/compilers/test_ast.py
@@ -404,6 +404,12 @@ def test_ast_tuple():
     assert type(code) == ast.Tuple
 
 
+def test_argument_destructuring():
+    """ Ensure argument destructuring compilers. """
+    can_compile("(fn [[a b]] (print a b))")
+    cant_compile("(fn [[]] 0)")
+
+
 def test_lambda_list_keywords_rest():
     """ Ensure we can compile functions with lambda list keywords."""
     can_compile("(fn (x &rest xs) (print xs))")
diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy
--- a/tests/native_tests/language.hy
+++ b/tests/native_tests/language.hy
@@ -1153,3 +1153,8 @@
   (assert
    (= (identify-keywords 1 "bloo" :foo)
       ["other" "other" "keyword"])))
+
+(defn test-argument-destr []
+  "Make sure argument destructuring works"
+  (defn f [[a b] [c]] (, a b c))
+  (assert (= (f [1 2] [3]) (, 1 2 3))))