Repository: hylang/hy

Issue:
All shadowed operators should have the same arities as their counterparts.
Probably one of them got updated without the other:

``` Hy
=> (+)
0
=> ((lambda [op] (op)) +)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 1, in <lambda>
  File "c:\users\me\code\repos\hy/hy/core/shadow.hy", line 27, in +
    (defn + [&rest args]
  File "c:\users\me\code\repos\hy/hy/core/shadow.hy", line 31, in _hy_anon_fn_1
    (raise (TypeError "Need at least 1 argument to add/concatenate"))
TypeError: Need at least 1 argument to add/concatenate
```

We should check them all though.



Discussion:
I noticed when I was reading hy.core.shadow this weekend that `+` demands an argument but `*` doesn't. That is, we provide the empty product but not the empty sum. I thought about it and realized that the problem with nullary `+` is that Python uses `+` for both addition and concatenation, so in a nullary context, we should return `[]`, `()`, `""`, or `0`, but we can't know which. It seems best to just disallow nullary `+`. So I think the shadowed version is right and the operator version is wrong.

For `/`, there's again inconsistency between the operator (which returns `1.`) and the shadowed version (which requires two arguments). It's not clear which is right, though. I'm leaning towards requiring the arguments, because although `1.` is the only sane value for the "empty quotient", I can't think of a context where asking for an empty quotient wouldn't be a mistake.

For `-`, the operator and shadowed version both require an argument, which seems fine, but not consistent with allowing nullary `/` if we decide to keep doing that.

Here's what some other Lisps do for `-` and `/`:
- In Clojure and Common Lisp, both `-` and `/` require an argument. Unary `/` is allowed, and returns the reciprocal.
- In Emacs Lisp, nullary `-` returns 0, but nullary `/` isn't allowed. Unary `/` returns its argument unchanged.

I think we should follow Clojure and Common Lisp. (We already have unary `/` return the reciprocal.)

> I thought about it and realized that the problem with nullary `+` is that Python uses `+` for both addition and concatenation, so in a nullary context, we should return `[]`, `()`, `""`, or `0`, but we can't know which. It seems best to just disallow nullary `+`. So I think the shadowed version is right and the operator version is wrong.

You could make the same argument against `*`. It also works on strings, tuples, and lists:

``` Hy
=> (* 3 "1")
'111'
=> (* 3 [1])
[1, 1, 1]
=> (* 3 (, 1))
(1, 1, 1)
```

In fact, any class can implement operators using dunder methods.

I don't think that's a good reason to disallow nullary `+`. Look at what ClojureScript does:

``` Clojure
cljs.user=> (+ "a" "b")
"ab"
cljs.user=> (+)
0
```

I'm inclined to do things Clojure's way when Python doesn't object.

> You could make the same argument against `*`.

The difference is that 1 is the identity element for both uses of `*` (multiplication and replication), whereas 0 isn't the identity element for concatenation.

What do you think about the cases of `-` and `/`?

That's a fair point. But every Lisp I've asked says `(+)` is `0`, including ClojureScript, which uses `+` for more than numbers. I think that's good enough. We could conceivably create an additive identity singleton that returns whatever it's added to and have `(+)` return that, similarly for `(*)`. But then does it break when you use other operators that `0` understands? Maybe it could be a special case of `0` the same way `false` is. I'm not sure it's worth the trouble.

I don't know why nullary `/` is allowed in Hy. That doesn't really make sense. @kirbyfan64, was this a mistake when you added the unary reciprocal case I asked for? If so, we should probably remove the nullary case. If there's an argument for it I'd like to hear it, since it may also apply to `-`.

Yeah, I think you've convinced me. Let's keep nullary `+` returning 0, and make the shadowed version consistent.


Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -371,7 +371,7 @@ def checker(self, expression):
 class HyASTCompiler(object):
 
     def __init__(self, module_name):
-        self.allow_builtins = False
+        self.allow_builtins = module_name.startswith("hy.core")
         self.anon_fn_count = 0
         self.anon_var_count = 0
         self.imports = defaultdict(set)
@@ -1892,7 +1892,7 @@ def _compile_compare_op_expression(self, expression):
                                  col_offset=e.start_column)
 
     @builds("=")
-    @builds("!=")
+    @builds("is")
     @builds("<")
     @builds("<=")
     @builds(">")
@@ -1900,33 +1900,25 @@ def _compile_compare_op_expression(self, expression):
     @checkargs(min=1)
     def compile_compare_op_expression(self, expression):
         if len(expression) == 2:
-            rval = "True"
-            if expression[0] == "!=":
-                rval = "False"
-            return ast.Name(id=rval,
+            return ast.Name(id="True",
                             ctx=ast.Load(),
                             lineno=expression.start_line,
                             col_offset=expression.start_column)
         return self._compile_compare_op_expression(expression)
 
-    @builds("is")
-    @builds("in")
+    @builds("!=")
     @builds("is_not")
-    @builds("not_in")
     @checkargs(min=2)
     def compile_compare_op_expression_coll(self, expression):
         return self._compile_compare_op_expression(expression)
 
-    @builds("%")
-    @builds("**")
-    @builds("<<")
-    @builds(">>")
-    @builds("|")
-    @builds("^")
-    @builds("&")
-    @builds_if("@", PY35)
-    @checkargs(min=2)
-    def compile_maths_expression(self, expression):
+    @builds("in")
+    @builds("not_in")
+    @checkargs(2)
+    def compile_compare_op_expression_binary(self, expression):
+        return self._compile_compare_op_expression(expression)
+
+    def _compile_maths_expression(self, expression):
         ops = {"+": ast.Add,
                "/": ast.Div,
                "//": ast.FloorDiv,
@@ -1942,14 +1934,18 @@ def compile_maths_expression(self, expression):
         if PY35:
             ops.update({"@": ast.MatMult})
 
-        inv = expression.pop(0)
-        op = ops[inv]
+        op = ops[expression.pop(0)]
+        right_associative = op == ast.Pow
 
+        if right_associative:
+            expression = expression[::-1]
         ret = self.compile(expression.pop(0))
         for child in expression:
             left_expr = ret.force_expr
             ret += self.compile(child)
             right_expr = ret.force_expr
+            if right_associative:
+                left_expr, right_expr = right_expr, left_expr
             ret += ast.BinOp(left=left_expr,
                              op=op(),
                              right=right_expr,
@@ -1957,27 +1953,46 @@ def compile_maths_expression(self, expression):
                              col_offset=child.start_column)
         return ret
 
-    @builds("*")
-    @builds("/")
+    @builds("**")
     @builds("//")
+    @builds("<<")
+    @builds(">>")
+    @builds("&")
+    @checkargs(min=2)
+    def compile_maths_expression_2_or_more(self, expression):
+        return self._compile_maths_expression(expression)
+
+    @builds("%")
+    @builds("^")
+    @checkargs(2)
+    def compile_maths_expression_exactly_2(self, expression):
+        return self._compile_maths_expression(expression)
+
+    @builds("*")
+    @builds("|")
     def compile_maths_expression_mul(self, expression):
-        if len(expression) > 2:
-            return self.compile_maths_expression(expression)
+        id_elem = {"*": 1, "|": 0}[expression[0]]
+        if len(expression) == 1:
+            return ast.Num(n=long_type(id_elem),
+                           lineno=expression.start_line,
+                           col_offset=expression.start_column)
+        elif len(expression) == 2:
+            return self.compile(expression[1])
         else:
-            id_op = {"*": HyInteger(1), "/": HyInteger(1), "//": HyInteger(1)}
+            return self._compile_maths_expression(expression)
 
-            op = expression.pop(0)
-            arg = expression.pop(0) if expression else id_op[op]
-            expr = HyExpression([
-                HySymbol(op),
-                id_op[op],
-                arg
-            ]).replace(expression)
-            return self.compile_maths_expression(expr)
+    @builds("/")
+    @checkargs(min=1)
+    def compile_maths_expression_div(self, expression):
+        if len(expression) == 2:
+            expression = HyExpression([HySymbol("/"),
+                                       HyInteger(1),
+                                       expression[1]]).replace(expression)
+        return self._compile_maths_expression(expression)
 
-    def compile_maths_expression_additive(self, expression):
+    def _compile_maths_expression_additive(self, expression):
         if len(expression) > 2:
-            return self.compile_maths_expression(expression)
+            return self._compile_maths_expression(expression)
         else:
             op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]()
             arg = expression.pop(0)
@@ -1988,6 +2003,17 @@ def compile_maths_expression_additive(self, expression):
                                col_offset=arg.start_column)
             return ret
 
+    @builds("&")
+    @builds_if("@", PY35)
+    @checkargs(min=1)
+    def compile_maths_expression_unary_idempotent(self, expression):
+        if len(expression) == 2:
+            # Used as a unary operator, this operator simply
+            # returns its argument.
+            return self.compile(expression[1])
+        else:
+            return self._compile_maths_expression(expression)
+
     @builds("+")
     def compile_maths_expression_add(self, expression):
         if len(expression) == 1:
@@ -1996,12 +2022,12 @@ def compile_maths_expression_add(self, expression):
                            lineno=expression.start_line,
                            col_offset=expression.start_column)
         else:
-            return self.compile_maths_expression_additive(expression)
+            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)
+        return self._compile_maths_expression_additive(expression)
 
     @builds("+=")
     @builds("/=")


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -12,7 +12,7 @@
 from .native_tests.with_decorator import *  # noqa
 from .native_tests.core import *  # noqa
 from .native_tests.reader_macros import *  # noqa
-from .native_tests.shadow import *  # noqa
+from .native_tests.operators import *  # noqa
 from .native_tests.with_test import *  # noqa
 from .native_tests.extra.anaphoric import *  # noqa
 from .native_tests.contrib.loop import *  # noqa
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
@@ -498,7 +498,7 @@ def test_compile_error():
     try:
         can_compile("(fn [] (in [1 2 3]))")
     except HyTypeError as e:
-        assert(e.message == "`in' needs at least 2 arguments, got 1.")
+        assert(e.message == "`in' needs 2 arguments, got 1")
     else:
         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
@@ -240,57 +240,6 @@
   (assert (= fact 120)))
 
 
-(defn test-not []
-  "NATIVE: test not"
-  (assert (not (= 1 2)))
-  (assert (= True (not False)))
-  (assert (= False (not 42))) )
-
-
-(defn test-inv []
-  "NATIVE: test inv"
-  (assert (= (~ 1) -2))
-  (assert (= (~ -2) 1)))
-
-
-(defn test-in []
-  "NATIVE: test in"
-  (assert (in "a" ["a" "b" "c" "d"]))
-  (assert (not-in "f" ["a" "b" "c" "d"])))
-
-
-(defn test-noteq []
-  "NATIVE: not eq"
-  (assert (!= 2 3))
-  (assert (not (!= 1))))
-
-
-(defn test-eq []
-  "NATIVE: eq"
-  (assert (= 1 1))
-  (assert (= 1)))
-
-
-(defn test-numops []
-  "NATIVE: test numpos"
-  (assert (> 5 4 3 2 1))
-  (assert (> 1))
-  (assert (< 1 2 3 4 5))
-  (assert (< 1))
-  (assert (<= 5 5 5 5 ))
-  (assert (<= 1))
-  (assert (>= 5 5 5 5 ))
-  (assert (>= 1)))
-
-
-(defn test-is []
-  "NATIVE: test is can deal with None"
-  (setv a None)
-  (assert (is a None))
-  (assert (is-not a "b"))
-  (assert (none? a)))
-
-
 (defn test-branching []
   "NATIVE: test if branching"
   (if True
diff --git a/tests/native_tests/operators.hy b/tests/native_tests/operators.hy
new file mode 100644
--- /dev/null
+++ b/tests/native_tests/operators.hy
@@ -0,0 +1,286 @@
+(import [hy._compat [PY35]])
+
+(defmacro op-and-shadow-test [op &rest body]
+  ; Creates two tests with the given `body`, one where all occurrences
+  ; of the symbol `f` are syntactically replaced with `op` (a test of
+  ; the real operator), and one where the body is preceded by an
+  ; assignment of `op` to `f` (a test of the shadow operator).
+  ;
+  ; `op` can also be a list of operators, in which case two tests are
+  ; created for each operator.
+  (import [hy [HySymbol HyString]] [hy.contrib.walk [prewalk]])
+  (setv defns [])
+  (for [o (if (coll? op) op [op])]
+    (.append defns `(defn ~(HySymbol (+ "test_operator_" o "_real")) []
+      (setv f-name ~(HyString o))
+      ~@(prewalk :form body :f (fn [x]
+        (if (and (symbol? x) (= x "f")) o x)))))
+    (.append defns `(defn ~(HySymbol (+ "test_operator_" o "_shadow")) []
+      (setv f-name ~(HyString o))
+      (setv f ~o)
+      ~@body)))
+  `(do ~@defns))
+
+(defmacro forbid [expr]
+  `(assert (try
+    (eval '~expr)
+    (except [TypeError] True)
+    (else (raise AssertionError)))))
+
+
+(op-and-shadow-test +
+
+  (assert (= (f) 0))
+
+  (defclass C [object] [__pos__ (fn [self] "called __pos__")])
+  (assert (= (f (C)) "called __pos__"))
+
+  (assert (= (f 1 2) 3))
+  (assert (= (f 1 2 3 4) 10))
+  (assert (= (f 1 2 3 4 5) 15))
+
+  ; with strings
+  (assert (= (f "a" "b" "c")
+             "abc"))
+  ; with lists
+  (assert (= (f ["a"] ["b"] ["c"])
+             ["a" "b" "c"])))
+
+
+(op-and-shadow-test -
+  (forbid (f))
+  (assert (= (f 1) -1))
+  (assert (= (f 2 1) 1))
+  (assert (= (f 2 1 1) 0)))
+
+
+(op-and-shadow-test *
+  (assert (= (f) 1))
+  (assert (= (f 3) 3))
+  (assert (= (f 3 3) 9))
+  (assert (= (f 2 3 4) 24))
+  (assert (= (f "ke" 4) "kekekeke"))
+  (assert (= (f [1 2 3] 2) [1 2 3 1 2 3])))
+
+
+(op-and-shadow-test **
+  (forbid (f))
+  (forbid (f 1))
+  (assert (= (f 3 2) 9))
+  (assert (= (f 5 4 3 2) (** 5 (** 4 (** 3 2))))))
+    ; Exponentiation is right-associative.
+
+
+(op-and-shadow-test /
+  (forbid (f))
+  (assert (= (f 2) .5))
+  (assert (= (f 3 2) 1.5))
+  (assert (= (f 8 2) 4))
+  (assert (= (f 8 2 2) 2))
+  (assert (= (f 8 2 2 2) 1)))
+
+
+(op-and-shadow-test //
+  (forbid (f))
+  (forbid (f 1))
+  (assert (= (f 16 5) 3))
+  (assert (= (f 8 2) 4))
+  (assert (= (f 8 2 2) 2)))
+
+
+(op-and-shadow-test %
+  (forbid (f))
+  (forbid (f 1))
+  (assert (= (f 16 5) 1))
+  (assert (= (f 8 2) 0))
+  (assert (= (f "aa %s bb" 15) "aa 15 bb"))
+  (assert (= (f "aa %s bb %s cc" (, "X" "Y")) "aa X bb Y cc"))
+  (forbid (f 1 2 3)))
+
+
+(when PY35 (op-and-shadow-test @
+  (defclass C [object] [
+    __init__ (fn [self content] (setv self.content content))
+    __matmul__ (fn [self other] (C (+ self.content other.content)))])
+  (forbid (f))
+  (assert (do (setv c (C "a")) (is (f c) c)))
+  (assert (= (. (f (C "b") (C "c")) content) "bc"))
+  (assert (= (. (f (C "d") (C "e") (C "f")) content) "def"))))
+
+
+(op-and-shadow-test <<
+  (forbid (f))
+  (forbid (f 1))
+  (assert (= (f 0b101 2) 0b10100))
+  (assert (= (f 0b101 2 3) 0b10100000)))
+
+
+(op-and-shadow-test >>
+  (forbid (f))
+  (forbid (f 1))
+  (assert (= (f 0b101 2) 0b1))
+  (assert (= (f 0b101000010 2 3) 0b1010)))
+
+
+(op-and-shadow-test &
+  (forbid (f))
+    ; Binary AND has no identity element for the set of all
+    ; nonnegative integers, because we can't have a 1 in every bit
+    ; when there are infinitely many bits.
+  (assert (= (f 17) 17))
+  (assert (= (f 0b0011 0b0101) 0b0001))
+  (assert (= (f 0b111 0b110 0b100) 0b100)))
+
+
+(op-and-shadow-test |
+  (assert (= (f) 0))
+  (assert (= (f 17) 17))
+  (assert (= (f 0b0011 0b0101) 0b0111))
+  (assert (= (f 0b11100 0b11000 0b10010) 0b11110)))
+
+
+(op-and-shadow-test ^
+  (forbid (f))
+  (forbid (f 17))
+  (assert (= (f 0b0011 0b0101) 0b0110))
+  (forbid (f 0b111 0b110 0b100)))
+    ; `xor` with 3 arguments is kill (https://github.com/hylang/hy/pull/1102),
+    ; so we don't allow `^` with 3 arguments, either.
+
+
+(op-and-shadow-test ~
+  (forbid (f))
+  (assert (= (f (chr 0b00101111)
+                (chr 0b11010000))))
+  (forbid (f (chr 0b00101111) (chr 0b11010000))))
+
+
+(op-and-shadow-test <
+
+  (forbid (f))
+  (assert (is (f "hello") True))
+  (assert (is (f 1 2) True))
+  (assert (is (f 2 1) False))
+  (assert (is (f 1 1) False))
+  (assert (is (f 1 2 3) True))
+  (assert (is (f 3 2 1) False))
+  (assert (is (f 1 3 2) False))
+  (assert (is (f 1 2 2) False))
+
+  ; Make sure chained comparisons use `and`, not `&`.
+  ; https://github.com/hylang/hy/issues/1191
+  (defclass C [object] [
+    __init__ (fn [self x]
+      (setv self.x x))
+    __lt__ (fn [self other]
+      self.x)])
+  (assert (= (f (C "a") (C "b") (C "c")) "b")))
+
+
+(op-and-shadow-test >
+  (forbid (f))
+  (assert (is (f "hello") True))
+  (assert (is (f 1 2) False))
+  (assert (is (f 2 1) True))
+  (assert (is (f 1 1) False))
+  (assert (is (f 1 2 3) False))
+  (assert (is (f 3 2 1) True))
+  (assert (is (f 1 3 2) False))
+  (assert (is (f 2 1 1) False)))
+
+
+(op-and-shadow-test <=
+  (forbid (f))
+  (assert (is (f "hello") True))
+  (assert (is (f 1 2) True))
+  (assert (is (f 2 1) False))
+  (assert (is (f 1 1) True))
+  (assert (is (f 1 2 3) True))
+  (assert (is (f 3 2 1) False))
+  (assert (is (f 1 3 2) False))
+  (assert (is (f 1 2 2) True)))
+
+
+(op-and-shadow-test >=
+  (forbid (f))
+  (assert (is (f "hello") True))
+  (assert (is (f 1 2) False))
+  (assert (is (f 2 1) True))
+  (assert (is (f 1 1) True))
+  (assert (is (f 1 2 3) False))
+  (assert (is (f 3 2 1) True))
+  (assert (is (f 1 3 2) False))
+  (assert (is (f 2 1 1) True)))
+
+
+(op-and-shadow-test [= is]
+  (forbid (f))
+  (assert (is (f "hello") True))
+  (defclass C)
+  (setv x (get {"is" (C) "=" 0} f-name))
+  (setv y (get {"is" (C) "=" 1} f-name))
+  (assert (is (f x x) True))
+  (assert (is (f y y) True))
+  (assert (is (f x y) False))
+  (assert (is (f y x) False))
+  (assert (is (f x x x x x) True))
+  (assert (is (f x x x y x) False))
+  (setv n None)
+  (assert (is (f n None) True))
+  (assert (is (f n "b") False)))
+
+
+(op-and-shadow-test [!= is-not]
+  (forbid (f))
+  (forbid (f "hello"))
+  (defclass C)
+  (setv x (get {"is_not" (C) "!=" 0} f-name))
+  (setv y (get {"is_not" (C) "!=" 1} f-name))
+  (setv z (get {"is_not" (C) "!=" 2} f-name))
+  (assert (is (f x x) False))
+  (assert (is (f y y) False))
+  (assert (is (f x y) True))
+  (assert (is (f y x) True))
+  (assert (is (f x y z) True))
+  (assert (is (f x x x) False))
+  (assert (is (f x y x) True))
+  (assert (is (f x x y) False)))
+
+
+(op-and-shadow-test and
+  (assert (is (f) True))
+  (assert (= (f 17) 17))
+  (assert (= (f 1 2) 2))
+  (assert (= (f 1 0) 0))
+  (assert (= (f 0 2) 0))
+  (assert (= (f 0 0) 0))
+  (assert (= (f 1 2 3) 3))
+  (assert (= (f 1 0 3) 0))
+  (assert (= (f "a" 1 True [1]) [1])))
+
+
+(op-and-shadow-test or
+  (assert (is (f) None))
+  (assert (= (f 17) 17))
+  (assert (= (f 1 2) 1))
+  (assert (= (f 1 0) 1))
+  (assert (= (f 0 2) 2))
+  (assert (= (f 0 0) 0))
+  (assert (= (f 1 2 3) 1))
+  (assert (= (f 0 0 3) 3))
+  (assert (= (f "" None 0 False []) [])))
+
+
+(op-and-shadow-test not
+  (forbid (f))
+  (assert (is (f "hello") False))
+  (assert (is (f 0) True))
+  (assert (is (f None) True)))
+
+
+(op-and-shadow-test [in not-in]
+  (forbid (f))
+  (forbid (f 3))
+  (assert (is (f 3 [1 2]) (!= f-name "in")))
+  (assert (is (f 2 [1 2]) (= f-name "in")))
+  (forbid (f 2 [1 2] [3 4])))
diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy
deleted file mode 100644
--- a/tests/native_tests/shadow.hy
+++ /dev/null
@@ -1,101 +0,0 @@
-(defn test-shadow-addition []
-  "NATIVE: test shadow addition"
-  (setv x +)
-  (assert (try
-           (x)
-           (except [TypeError] True)
-           (else (raise AssertionError))))
-  (assert (= (x 1 2 3 4) 10))
-  (assert (= (x 1 2 3 4 5) 15))
-  ; with strings
-  (assert (= (x "a" "b" "c")
-             "abc"))
-  ; with lists
-  (assert (= (x ["a"] ["b"] ["c"])
-             ["a" "b" "c"])))
-
-
-(defn test-shadow-subtraction []
-  "NATIVE: test shadow subtraction"
-  (setv x -)
-  (assert (try
-           (x)
-           (except [TypeError] True)
-           (else (raise AssertionError))))
-  (assert (= (x 1) -1))
-  (assert (= (x 2 1) 1))
-  (assert (= (x 2 1 1) 0)))
-
-
-(defn test-shadow-multiplication []
-  "NATIVE: test shadow multiplication"
-  (setv x *)
-  (assert (= (x) 1))
-  (assert (= (x 3) 3))
-  (assert (= (x 3 3) 9)))
-
-
-(defn test-shadow-division []
-  "NATIVE: test shadow division"
-  (setv x /)
-  (assert (try
-           (x)
-           (except [TypeError] True)
-           (else (raise AssertionError))))
-  (assert (= (x 1) 1))
-  (assert (= (x 8 2) 4))
-  (assert (= (x 8 2 2) 2))
-  (assert (= (x 8 2 2 2) 1)))
-
-
-(defn test-shadow-compare []
-  "NATIVE: test shadow compare"
-
-  (for [x [< <= = != >= >]]
-     (assert (try
-              (x)
-              (except [TypeError] True)
-              (else (raise AssertionError))))
-     (assert (try
-              (x 1)
-              (except [TypeError] True)
-              (else (raise AssertionError)))))
-
-  (for [(, x y) [[< >=]
-                 [<= >]
-                 [= !=]]]
-    (for [args [[1 2]
-                [2 1]
-                [1 1]
-                [2 2]]]
-      (assert (= (apply x args) (not (apply y args))))))
-
-  (setv s-lt <
-        s-gt >
-        s-le <=
-        s-ge >=
-        s-eq =
-        s-ne !=)
-  (assert (apply s-lt [1 2 3]))
-  (assert (not (apply s-lt [3 2 1])))
-  (assert (apply s-gt [3 2 1]))
-  (assert (not (apply s-gt [1 2 3])))
-  (assert (apply s-le [1 1 2 2 3 3]))
-  (assert (not (apply s-le [1 1 2 2 1 1])))
-  (assert (apply s-ge [3 3 2 2 1 1]))
-  (assert (not (apply s-ge [3 3 2 2 3 3])))
-  (assert (apply s-eq [1 1 1 1 1]))
-  (assert (not (apply s-eq [1 1 2 1 1])))
-  (assert (apply s-ne [1 2 3 4 5]))
-  (assert (not (apply s-ne [1 1 2 3 4])))
-
-  ; Make sure chained comparisons use `and`, not `&`.
-  ; https://github.com/hylang/hy/issues/1191
-  (defclass C [object] [
-    __init__ (fn [self x]
-      (setv self.x x))
-    __lt__ (fn [self other]
-      self.x)])
-  (assert (= (< (C "a") (C "b") (C "c")) "b"))
-  (setv f <)
-  (assert (= (f (C "a") (C "b") (C "c")) "b")))