Repository: hylang/hy

Issue:
Comparison operators should accept more arities
In Clojure:

``` Clojure
user=> (< 1)
true
```

Common Lisp:

``` Lisp
* (< 1)

T
```

Hy:

``` Hy
=> (< 1)
  File "<input>", line 1, column 1

  (< 1)
  ^---^
HyTypeError: `<' needs at least 2 arguments, got 1.
```

It also works like this for the other comparison operators, `<, <=, >, >=, <>, !=, ==`. (Perhaps we should even consider the remaining "comparisions" `in, not in, is, is not`.)

Again I am surprised. Why is Hy different? Judging from past issues like this (for `and, or, +, -, /` etc.) I think it's just because nobody thought of it. 

Perhaps it would not be an invalid choice if this were actually a conscious choice, because we have the precedent of Scheme requiring at least two arguments in its comparison functions.

So let's make it a conscious choice.  What's the rationale for a minimum of two arguments vs. one? Is it a good enough reason to make Hy work like Scheme in this regard, instead of more like Clojure and Common Lisp like we've done for most everything else? 



Discussion:
Interesting. Why would one want to compare if 1 is smaller than? I can understand comparing more than two: `(< 1 5 7)` to check that they're in ascending order. What would be the actual usage of this?

Macros:

``` hy
(defmacro blah [&rest args]
  `(< ~@args))
(blah 1)
(blah 1 2)
```

Much simpler than if you'd have to check the length of args for each similar macro.

Right, makes sense then. What would value be for each of them, `true` for all? `true` for some and `false` for others?

`in, not in, is, is not` I would leave as they are now, unless there's some good reason to do it otherwise.

`true` for all but `!=`, which would be false, and the collection-related comparators (`in`, `not-in`, etc) I wouldn't touch, either.

I have a patch in the works, btw, just finishing up the tests.


Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -1774,18 +1774,7 @@ def make_assign(value, node=None):
                               values=[value.force_expr for value in values])
         return ret
 
-    @builds("=")
-    @builds("!=")
-    @builds("<")
-    @builds("<=")
-    @builds(">")
-    @builds(">=")
-    @builds("is")
-    @builds("in")
-    @builds("is_not")
-    @builds("not_in")
-    @checkargs(min=2)
-    def compile_compare_op_expression(self, expression):
+    def _compile_compare_op_expression(self, expression):
         ops = {"=": ast.Eq, "!=": ast.NotEq,
                "<": ast.Lt, "<=": ast.LtE,
                ">": ast.Gt, ">=": ast.GtE,
@@ -1805,6 +1794,32 @@ def compile_compare_op_expression(self, expression):
                                  lineno=e.start_line,
                                  col_offset=e.start_column)
 
+    @builds("=")
+    @builds("!=")
+    @builds("<")
+    @builds("<=")
+    @builds(">")
+    @builds(">=")
+    @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,
+                            ctx=ast.Load(),
+                            lineno=expression.start_line,
+                            col_offset=expression.start_column)
+        return self._compile_compare_op_expression(expression)
+
+    @builds("is")
+    @builds("in")
+    @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("<<")


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
@@ -468,9 +468,9 @@ def _compile_string(s):
 def test_compile_error():
     """Ensure we get compile error in tricky cases"""
     try:
-        can_compile("(fn [] (= 1))")
+        can_compile("(fn [] (in [1 2 3]))")
     except HyTypeError as e:
-        assert(e.message == "`=' needs at least 2 arguments, got 1.")
+        assert(e.message == "`in' needs at least 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
@@ -208,15 +208,26 @@
 
 (defn test-noteq []
   "NATIVE: not eq"
-  (assert (!= 2 3)))
+  (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 (>= 5 5 5 5 )))
+  (assert (<= 1))
+  (assert (>= 5 5 5 5 ))
+  (assert (>= 1)))
 
 
 (defn test-is []