Repository: hylang/hy

Issue:
How do we feel about removing tuple unpacking in lambda lists?
I'm working on #1573 and I've noticed that without this feature, lambda lists would be a lot simpler. The fundamental unit, particularly for mandatory arguments, would be just a symbol. As is, the fundamental unit can be a symbol or it can be an arbitrarily nested list of symbols. Currently, this is already broken because of the conflict with the syntax for default values (#1410). It's also worth noting that this feature was present in Python 2 but removed for Python 3; to bridge the gap, Hy implements it with temporary variables instead of Python 2's native support.

Unpacking, including much more general unpacking capabilities, could still be provided by macros (e.g., #1328).


Discussion:
Just to make sure, this means removing support for following or something completely else?
```
=> (defn foo [(, bar baz)]
...  (+ bar baz))
=> (setv test (, 1 2))
=> (foo test)
3
```
It's handy, but I don't see that so often. I don't have (strong) objections on removing it.

Yeah, that's what I mean. I didn't even realize you could write it as `(defn foo [(, bar baz)] …)` as well as `(defn foo [[bar baz]] …)`.
I guess one could in theory combine that with optional parameters:
```
(defn foo [&optional [(, bar baz) (, 1 2)]] ...)
```
But then we would have to have a special case for
```
(defn foo [&optional (, bar baz)] ...)
```
is both ```bar``` and ```baz``` ```None```? Or is the tuple ```None``` and unpacking will thrown an error? There might be other special cases too that I fail to think right now. I think it's better to remove it and do unpacking inside of the function.
#1328, (or its successor) could pretty much replace this. Let's get it out of the compiler. 

I stalled when implementing that mainly because of #1414. But now that we have a `let` macro I might do it differently anyway. I intentionally omitted destructuring in `let` because it would add complexity to an already complicated macro, but I thought it would be relatively straightforward to implement a `let=:` macro in terms of `let` and something like #1328.

Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -553,6 +553,8 @@ def _parse_lambda_list(self, exprs):
                 continue
 
             if lambda_keyword is None:
+                if not isinstance(expr, HySymbol):
+                    raise HyTypeError(expr, "Parameters must be symbols")
                 args.append(expr)
             elif lambda_keyword == "&rest":
                 if varargs:
@@ -1893,19 +1895,6 @@ def compile_function_def(self, expression):
 
         (ret, args, defaults, stararg,
          kwonlyargs, kwonlydefaults, 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([
-                        HySymbol("setv"), arg, var
-                    ])]
-                ) + expression
-                expression = expression.replace(arg[0])
 
         # Before Python 3.7, docstrings must come at the start, so ensure that
         # happens even if we generate anonymous variables.


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
@@ -414,12 +414,6 @@ 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
@@ -1761,11 +1761,6 @@ macros()
    (= (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))))
-
 #@(pytest.mark.xfail
 (defn test-assert-multistatements []
   ; https://github.com/hylang/hy/issues/1390
@@ -1789,21 +1784,11 @@ macros()
   (defn f [] "docstring" 5)
   (assert (= (. f __doc__) "docstring"))
 
-  ; destructuring and the implicit variables it creates
-  ; shouldn't interfere with docstrings
-  ; (https://github.com/hylang/hy/issues/1409)
-  (defn f2 [[a b]] "docstring" 5)
-  (assert (= (. f2 __doc__) "docstring"))
-
   ; a single string is the return value, not a docstring
   ; (https://github.com/hylang/hy/issues/1402)
   (defn f3 [] "not a docstring")
   (assert (none? (. f3 __doc__)))
-  (assert (= (f3) "not a docstring"))
-
-  (defn f4 [[a b]] "not a docstring")
-  (assert (none? (. f4 __doc__)))
-  (assert (= (f4 [1 2]) "not a docstring")))
+  (assert (= (f3) "not a docstring")))
 
 (defn test-module-docstring []
   (import [tests.resources.module-docstring-example :as m])