Repository: hylang/hy

Issue:
Do we *really* want setv to return a tuple?
Currently `setv` returns a tuple. Sometimes.

``` Hy
=> (print (setv x 7 y 42))
(7, 42)
=> (print (setv x 42))  ; shouldn't it print (42,)?
42
=> (print (setv x 7 x 42)  ; wouldn't you expect (7, 42)?
(42, 42)
```

I propose changing it to just return the value of the last assignment. This is more consistent with the way a single assignment works. If you assign to the same name more than once, it gets lost anyway, which is surprising. This is also more consistent with Common Lisp's `setq` which we've based `setv` on.

``` Lisp
* (setq x 42 x 42)
;; [dire warnings about using setq at toplevel]
42
*
```

It would also simplify my proposed `as->` macro. #1047 .



Discussion:
Seems like this a duplicate (or very closely related) of #249?

It seems like the behavior may have changed since this was originally opened. When I run the above

``` hy
=> (print (setv x 7 y 42))
  File "<input>", line 1, column 8

  (print (setv x 7 y 42))
         ^-------------^
HyTypeError: `setv' needs 2 arguments, got 4
=> (print (setv x 42))
42
=> (print (setv x 7 x 42))
  File "<input>", line 1, column 8

  (print (setv x 7 x 42))
         ^-------------^
HyTypeError: `setv' needs 2 arguments, got 4
```

From the other issue

``` hy
=> (print (setv (, a b) [1 2]))                 
(1, 2)
```

> It seems like the behavior may have changed since this was originally opened. 

@shellhead No, it's the opposite. `setv` has changed to allow multiple pairs from only one pair in the old PyPI version that you still seem to be using. Use the current github version for the new `setv`.

Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -2156,16 +2156,9 @@ def compile_def_expression(self, expression):
                                   root))
         else:
             result = Result()
-            exprs = []
             for tgt, target in zip(expression[::2], expression[1::2]):
-                item = self._compile_assign(tgt, target,
-                                            tgt.start_line, tgt.start_column)
-                result += item
-                exprs.append(item.force_expr)
-
-            result += ast.Tuple(elts=exprs, lineno=expression.start_line,
-                                col_offset=expression.start_column,
-                                ctx=ast.Load())
+                result += self._compile_assign(tgt, target, tgt.start_line,
+                                               tgt.start_column)
             return result
 
     def _compile_assign(self, name, result,
@@ -2196,7 +2189,6 @@ def _compile_assign(self, name, result,
                 targets=[st_name],
                 value=result.force_expr)
 
-        result += ld_name
         return result
 
     @builds("for*")


Test patch:
diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy
--- a/tests/native_tests/core.hy
+++ b/tests/native_tests/core.hy
@@ -81,10 +81,12 @@
   (setv x 1)
   (setv y 1)
   (assert-equal x y)
-  (setv x (setv y  12))
+  (setv y 12)
+  (setv x y)
   (assert-equal x 12)
   (assert-equal y 12)
-  (setv x (setv y (fn [x] 9)))
+  (setv y (fn [x] 9))
+  (setv x y)
   (assert-equal (x y) 9)
   (assert-equal (y x) 9)
   (try (do (setv a.b 1) (assert False))
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
@@ -51,11 +51,6 @@
   (assert (= #{} (set))))
 
 
-(defn test-setv-empty []
-  "NATIVE: test setv works with no arguments"
-  (assert (is (setv) None)))
-
-
 (defn test-setv-get []
   "NATIVE: test setv works on a get expression"
   (setv foo [0 1 2])
@@ -81,15 +76,49 @@
 
 (defn test-setv-pairs []
   "NATIVE: test that setv works on pairs of arguments"
-  (assert (= (setv a 1 b 2) (, 1 2)))
+  (setv a 1 b 2)
   (assert (= a 1))
   (assert (= b 2))
   (setv y 0 x 1 y x)
-  (assert y)
+  (assert (= y 1))
   (try (eval '(setv a 1 b))
        (except [e [TypeError]] (assert (in "`setv' needs an even number of arguments" (str e))))))
 
 
+(defn test-setv-returns-none []
+  "NATIVE: test that setv always returns None"
+
+  (assert (none? (setv)))
+  (assert (none? (setv x 1)))
+  (assert (= x 1))
+  (assert (none? (setv x 2)))
+  (assert (= x 2))
+  (assert (none? (setv y 2  z 3)))
+  (assert (= y 2))
+  (assert (= z 3))
+  (assert (none? (setv [y z] [7 8])))
+  (assert (= y 7))
+  (assert (= z 8))
+  (assert (none? (setv (, y z) [9 10])))
+  (assert (= y 9))
+  (assert (= z 10))
+
+  (setv p 11)
+  (setv p (setv q 12))
+  (assert (= q 12))
+  (assert (none? p))
+
+  ; https://github.com/hylang/hy/issues/1052
+  (assert (none? (setv (get {} "x") 42)))
+  (setv l [])
+  (defclass Foo [object]
+    [__setattr__ (fn [self attr val]
+      (.append l [attr val]))])
+  (setv x (Foo))
+  (assert (none? (setv x.eggs "ham")))
+  (assert (not (hasattr x "eggs")))
+  (assert (= l [["eggs" "ham"]])))
+
 (defn test-store-errors []
   "NATIVE: test that setv raises the correct errors when given wrong argument types"
   (try
diff --git a/tests/native_tests/reader_macros.hy b/tests/native_tests/reader_macros.hy
--- a/tests/native_tests/reader_macros.hy
+++ b/tests/native_tests/reader_macros.hy
@@ -49,8 +49,8 @@
 (defn test-builtin-decorator-reader []
   (defn increment-arguments [func]
     "Increments each argument passed to the decorated function."
-    #@((wraps func)
-       (defn wrapper [&rest args &kwargs kwargs]
+    ((wraps func)
+       (fn [&rest args &kwargs kwargs]
          (apply func
                 (map inc args)
                 (dict-comp k (inc v) [[k v] (.items kwargs)])))))
diff --git a/tests/native_tests/with_decorator.hy b/tests/native_tests/with_decorator.hy
--- a/tests/native_tests/with_decorator.hy
+++ b/tests/native_tests/with_decorator.hy
@@ -39,8 +39,8 @@
 (defn test-decorator-clobbing []
   "NATIVE: Tests whether nested decorators work"
   (do
-    (defn dec1 [f] (defn k [] (+ (f) 1)))
-    (defn dec2 [f] (defn k [] (+ (f) 2)))
+    (defn dec1 [f] (fn [] (+ (f) 1)))
+    (defn dec2 [f] (fn [] (+ (f) 2)))
     (with-decorator dec1
       (with-decorator dec2
         (defn f [] 1)))