Repository: hylang/hy

Issue:
Hy sets are really broken.
``` Hy
(env-hy) C:\Users\ME\Code>hy
hy 0.11.0 using CPython(v3.4.3:9b73f1c3e601) 3.4.3 on Windows
=> #{:a 'a}
Traceback (most recent call last):
  File "C:\Python34\Scripts\env-hy\Scripts\hy-script.py", line 9, in <module>
    load_entry_point('hy==0.11.0', 'console_scripts', 'hy')()
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\cmdline.py", line 341, in hy_main
    sys.exit(cmdline_handler("hy", sys.argv))
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\cmdline.py", line 336, in cmdline_handler
    return run_repl(spy=options.spy)
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\cmdline.py", line 234, in run_repl
    os=platform.system()
  File "C:\Python34\Lib\code.py", line 234, in interact
    more = self.push(line)
  File "C:\Python34\Lib\code.py", line 256, in push
    more = self.runsource(source, self.filename)
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\cmdline.py", line 93, in runsource
    tokens = tokenize(source)
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\lex\__init__.py", line 33, in tokenize
    return parser.parse(lexer.lex(buf))
  File "C:\Python34\Scripts\env-hy\lib\site-packages\rply\parser.py", line 23, in parse
    t, symstack, statestack, state
  File "C:\Python34\Scripts\env-hy\lib\site-packages\rply\parser.py", line 80, in _reduce_production
    value = p.func(targ)
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\lex\parser.py", line 69, in wrapped
    ret = fun(p)
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\lex\parser.py", line 214, in t_set
    return HySet(p[1])
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\models\set.py", line 31, in __init__
    items = sorted(items)
TypeError: unorderable types: HyExpression() < HyKeyword()

(env-hy) C:\Users\ME\Code>
```

That is NOT supposed to happen.



Discussion:
Wow, I must've been delusional when I wrote that code. Sets have nothing to do with lists, yet I had the latter derive from the former...

So you basically end up with sets. That can have duplicates appended. Out of order. And can be indexed. Ugh...

As I understand, HySet is supposed to represent set literals, not sets themselves. So having duplicates, having a fixed order, and deriving from HyList is fine. HyDict does the same thing.


Resolving patch:
diff --git a/hy/models/set.py b/hy/models/set.py
--- a/hy/models/set.py
+++ b/hy/models/set.py
@@ -18,19 +18,16 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
+from hy.models import _wrappers, wrap_value
 from hy.models.list import HyList
-from functools import reduce
 
 
 class HySet(HyList):
     """
-    Hy set (actually a list that pretends to be a set)
+    Hy set (just a representation of a set)
     """
 
-    def __init__(self, items):
-        items = sorted(items)
-        items = list(reduce(lambda r, v: v in r and r or r+[v], items, []))
-        super(HySet, self).__init__(items)
-
     def __repr__(self):
         return "#{%s}" % (" ".join([repr(x) for x in self]))
+
+_wrappers[set] = lambda s: HySet(wrap_value(x) for x in s)


Test patch:
diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py
--- a/tests/lex/test_lex.py
+++ b/tests/lex/test_lex.py
@@ -230,6 +230,16 @@ def test_sets():
         HyExpression([HySymbol("baz"), HySymbol("quux")])
     ])]
 
+    # Duplicate items in a literal set should be okay (and should
+    # be preserved).
+    objs = tokenize("#{1 2 1 1 2 1}")
+    assert objs == [HySet([HyInteger(n) for n in [1, 2, 1, 1, 2, 1]])]
+    assert len(objs[0]) == 6
+
+    # https://github.com/hylang/hy/issues/1120
+    objs = tokenize("#{a 1}")
+    assert objs == [HySet([HySymbol("a"), HyInteger(1)])]
+
 
 def test_nospace():
     """ Ensure we can tokenize without spaces if we have to """
diff --git a/tests/models/test_set.py b/tests/models/test_set.py
--- a/tests/models/test_set.py
+++ b/tests/models/test_set.py
@@ -5,4 +5,4 @@
 
 
 def test_set():
-    assert hyset == [1, 2, 3]
+    assert hyset == [3, 1, 2, 2]
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
@@ -47,6 +47,7 @@
 (defn test-sets []
   "NATIVE: test sets work right"
   (assert (= #{1 2 3 4} (| #{1 2} #{3 4})))
+  (assert (= (type #{1 2 3 4}) set))
   (assert (= #{} (set))))
 
 
diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy
--- a/tests/native_tests/native_macros.hy
+++ b/tests/native_tests/native_macros.hy
@@ -39,6 +39,9 @@
 (defmacro a-dict [] {1 2})
 (assert (= (a-dict) {1 2}))
 
+(defmacro a-set [] #{1 2})
+(assert (= (a-set) #{1 2}))
+
 (defmacro a-none [])
 (assert (= (a-none) None))