Repository: hylang/hy

Issue:
Remove fraction literals
They don't have their own Python AST node, so there's no real need to give them special support in the core language. Hyrule could provide some kind of syntactic sugar for them, but the need for fractions is pretty rare anyway.


Discussion:

Resolving patch:
diff --git a/hy/__init__.py b/hy/__init__.py
--- a/hy/__init__.py
+++ b/hy/__init__.py
@@ -15,8 +15,6 @@ def _initialize_env_var(env_var, default_val):
 hy.importer._inject_builtins()
 # we import for side-effects.
 
-from fractions import Fraction as _Fraction  # For fraction literals
-
 # Import some names on demand so that the dependent modules don't have
 # to be loaded if they're not needed.
 
diff --git a/hy/lex/hy_reader.py b/hy/lex/hy_reader.py
--- a/hy/lex/hy_reader.py
+++ b/hy/lex/hy_reader.py
@@ -55,11 +55,6 @@ def symbol_like(ident, reader=None):
         return Integer(ident)
     except ValueError:
         pass
-    try:
-        num, denom = ident.split("/")
-        return mkexpr("hy._Fraction", Integer(num), Integer(denom))
-    except ValueError:
-        pass
     try:
         return Float(ident)
     except ValueError:
diff --git a/hy/models.py b/hy/models.py
--- a/hy/models.py
+++ b/hy/models.py
@@ -1,6 +1,5 @@
 import operator
 from contextlib import contextmanager
-from fractions import Fraction
 from functools import reduce
 from itertools import groupby
 from math import isinf, isnan
@@ -628,9 +627,6 @@ class Expression(Sequence):
 
 
 _wrappers[Expression] = recwrap(Expression)
-_wrappers[Fraction] = lambda e: Expression(
-    [Symbol("hy._Fraction"), as_model(e.numerator), as_model(e.denominator)]
-)
 
 
 class Set(Sequence):


Test patch:
diff --git a/tests/importer/test_importer.py b/tests/importer/test_importer.py
--- a/tests/importer/test_importer.py
+++ b/tests/importer/test_importer.py
@@ -2,7 +2,6 @@
 import importlib
 import runpy
 import sys
-from fractions import Fraction
 from importlib import reload
 from pathlib import Path
 
@@ -110,7 +109,6 @@ def eval_str(s):
     assert eval_str('{"dog" "bark" "cat" "meow"}') == {"dog": "bark", "cat": "meow"}
     assert eval_str("#(1 2 3)") == (1, 2, 3)
     assert eval_str("#{3 1 2}") == {1, 2, 3}
-    assert eval_str("1/2") == Fraction(1, 2)
     assert eval_str('(.strip " fooooo   ")') == "fooooo"
     assert (
         eval_str('(if True "this is if true" "this is if false")') == "this is if true"
diff --git a/tests/native_tests/hy_repr.hy b/tests/native_tests/hy_repr.hy
--- a/tests/native_tests/hy_repr.hy
+++ b/tests/native_tests/hy_repr.hy
@@ -5,6 +5,7 @@
   ; Test that a variety of objects round-trip from strings.
   (import
     collections [deque ChainMap OrderedDict]
+    fractions [Fraction]
     re)
 
   (for [original-str (lfor
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
@@ -5,7 +5,6 @@
         re
         operator [or_]
         itertools [repeat count islice]
-        fractions [Fraction]
         pickle
         typing [get-type-hints List Dict]
         asyncio
@@ -37,10 +36,6 @@
   (assert (= 0b1011101 93)))
 
 
-(defn test-fractions []
-  (assert (= 1/2 (Fraction 1 2))))
-
-
 (defn test-lists []
   (assert (= [1 2 3 4] (+ [1 2] [3 4]))))
 
@@ -1027,7 +1022,6 @@
   (for [x [
       None False True
       5 5.1
-      1/2
       5j 5.1j 2+1j 1.2+3.4j
       "" b""
       "apple bloom" b"apple bloom" "⚘" b"\x00"
diff --git a/tests/native_tests/let.hy b/tests/native_tests/let.hy
--- a/tests/native_tests/let.hy
+++ b/tests/native_tests/let.hy
@@ -148,7 +148,7 @@
     (assert (= foo 42))
     (try
       (do
-        1/0
+        (/ 1 0)
         (assert False))
       (except [foo Exception]
         ;; let bindings should work in except block
@@ -166,7 +166,7 @@
     (with [foo (pytest.raises ZeroDivisionError)]
       (do
         (assert (!= foo 42))
-        1/0
+        (/ 1 0)
         (assert False)))
     (assert (is (. foo type) ZeroDivisionError))))
 
diff --git a/tests/resources/hy_repr_str_tests.txt b/tests/resources/hy_repr_str_tests.txt
--- a/tests/resources/hy_repr_str_tests.txt
+++ b/tests/resources/hy_repr_str_tests.txt
@@ -16,8 +16,6 @@
 !-Inf
 !NaN
 
-!1/2
-
 !5j
 !5.1j
 !2+1j
@@ -25,6 +23,8 @@
 !Inf-Infj
 !NaN+NaNj
 
+!(Fraction 1 3)
+
 ;; * Symbols and keywords
 
 'mysymbol
diff --git a/tests/test_lex.py b/tests/test_lex.py
--- a/tests/test_lex.py
+++ b/tests/test_lex.py
@@ -164,12 +164,6 @@ def test_lex_integers():
     assert objs == [Integer(42)]
 
 
-def test_lex_fractions():
-    """Make sure that fractions are valid expressions"""
-    objs = tokenize("1/2")
-    assert objs == [Expression([Symbol("hy._Fraction"), Integer(1), Integer(2)])]
-
-
 def test_lex_expression_float():
     """Make sure expressions can produce floats"""
     objs = tokenize("(foo 2.)")
@@ -239,9 +233,6 @@ def test_lex_digit_separators():
 
     assert tokenize("1_2.3,4") == [Float(12.34)]
     assert tokenize("1_2e3,4") == [Float(12e34)]
-    assert tokenize("1,2/3_4") == [
-        Expression([Symbol("hy._Fraction"), Integer(12), Integer(34)])
-    ]
     assert tokenize("1,0_00j") == [Complex(1000j)]
 
     assert tokenize("1,,,,___,____,,__,,2__,,,__") == [Integer(12)]
@@ -281,10 +272,6 @@ def test_lex_bad_attrs():
     with lexe():
         tokenize("3.1+5.1j.foo")
     assert tokenize("j.foo")
-    with lexe():
-        tokenize("3/4.foo")
-    assert tokenize("a/1.foo")
-    assert tokenize("1/a.foo")
     with lexe():
         tokenize(":hello.foo")