Repository: hylang/hy

Issue:
(+ x) shouldn't compile to (0 + x)
Python has a unary `+` that invokes the `__pos__` method. This is not the same as the `__add__` method for `0`. Hy should be able to invoke this with `+` instead of requiring a direct call to `__pos__`.

Hy does handle unary `-` correctly though, I checked (`--spy`):

``` Hy
=> (- 1)
(- 1)
-1
```

Unary `+`? Nope.

``` Hy
=> (+ 1)
(0 + 1)
1
```

What should have happened:

``` Hy
=> (+ 1)
(+ 1)
1
```



Discussion:

Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -1910,7 +1910,6 @@ def compile_maths_expression(self, expression):
                              col_offset=child.start_column)
         return ret
 
-    @builds("+")
     @builds("*")
     @builds("/")
     @builds("//")
@@ -1918,8 +1917,7 @@ def compile_maths_expression_mul(self, expression):
         if len(expression) > 2:
             return self.compile_maths_expression(expression)
         else:
-            id_op = {"+": HyInteger(0), "*": HyInteger(1), "/": HyInteger(1),
-                     "//": HyInteger(1)}
+            id_op = {"*": HyInteger(1), "/": HyInteger(1), "//": HyInteger(1)}
 
             op = expression.pop(0)
             arg = expression.pop(0) if expression else id_op[op]
@@ -1930,20 +1928,34 @@ def compile_maths_expression_mul(self, expression):
             ]).replace(expression)
             return self.compile_maths_expression(expr)
 
-    @builds("-")
-    @checkargs(min=1)
-    def compile_maths_expression_sub(self, expression):
+    def compile_maths_expression_additive(self, expression):
         if len(expression) > 2:
             return self.compile_maths_expression(expression)
         else:
-            arg = expression[1]
+            op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]()
+            arg = expression.pop(0)
             ret = self.compile(arg)
-            ret += ast.UnaryOp(op=ast.USub(),
+            ret += ast.UnaryOp(op=op,
                                operand=ret.force_expr,
                                lineno=arg.start_line,
                                col_offset=arg.start_column)
             return ret
 
+    @builds("+")
+    def compile_maths_expression_add(self, expression):
+        if len(expression) == 1:
+            # Nullary +
+            return ast.Num(n=long_type(0),
+                           lineno=expression.start_line,
+                           col_offset=expression.start_column)
+        else:
+            return self.compile_maths_expression_additive(expression)
+
+    @builds("-")
+    @checkargs(min=1)
+    def compile_maths_expression_sub(self, expression):
+        return self.compile_maths_expression_additive(expression)
+
     @builds("+=")
     @builds("/=")
     @builds("//=")


Test patch:
diff --git a/tests/native_tests/mathematics.hy b/tests/native_tests/mathematics.hy
--- a/tests/native_tests/mathematics.hy
+++ b/tests/native_tests/mathematics.hy
@@ -28,6 +28,18 @@
                  (assert (= 0 (+)))))
 
 
+(defn test-add-unary []
+  "NATIVE: test that unary + calls __pos__"
+
+  (defclass X [object]
+    [__pos__ (fn [self] "called __pos__")])
+  (assert (= (+ (X)) "called __pos__"))
+
+  ; Make sure the shadowed version works, too.
+  (setv f +)
+  (assert (= (f (X)) "called __pos__")))
+
+
 (setv test_div (fn []
                  "NATIVE: Test division"
                  (assert (= 25 (/ 100 2 2)))
diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy
--- a/tests/native_tests/shadow.hy
+++ b/tests/native_tests/shadow.hy
@@ -8,13 +8,9 @@
     (assert (= (x 1 2 3 4) 10))
     (assert (= (x 1 2 3 4 5) 15))
     ; with strings
-    (assert (= (x "a")
-               "a"))
     (assert (= (x "a" "b" "c")
                "abc"))
     ; with lists
-    (assert (= (x ["a"])
-               ["a"]))
     (assert (= (x ["a"] ["b"] ["c"])
                ["a" "b" "c"]))))