Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unquote/unpacking improvements #1545

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions hy/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,28 +699,31 @@ def _render_quoted_form(self, form, level):
imports = set([name])

if isinstance(form, (HyList, HyDict, HySet)):
if not form:
contents = HyList()
else:
# If there are arguments, they can be spliced
# so we build a sum...
contents = HyExpression([HySymbol("+"), HyList()])

contents = HyList()
for x in form:
f_imports, f_contents, splice = self._render_quoted_form(x,
level)
imports.update(f_imports)
if splice:
to_add = HyExpression([
HySymbol("list"),
HyExpression([HySymbol("or"), f_contents, HyList()])])
# On Python 3.5 and newer, leverage PEP 448 to generate
# expression using tuple unpacking instead of addition.
#
# Should be marginally faster too.
contents.append(HyExpression([
HySymbol("unpack-iterable" if PY35 else "list"),
HyExpression([HySymbol("or"), f_contents, HyList()])
]))
else:
to_add = HyList([f_contents])
contents.append(f_contents if PY35
else HyList([f_contents]))

contents.append(to_add)
if not PY35:
# Without Python 3.5's unpacking improvements, we need
# to concatenate the expression manually
contents = HyExpression([HySymbol("sum"), contents, HyList()])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring of sum says "This function is intended specifically for use with numeric values and may reject non-numeric types." It's an implementation detail that this works at all. Other Python implementations or future Python versions may break this and a type checker has every right to flag this kind of use as a type error for not using numbers. Hy's + special form is already variadic, why not use that instead? Note that the unary + calls __pos__ instead of __add__, but you can force it to use the binary form by starting with an empty list.


return imports, HyExpression([HySymbol(name),
contents]).replace(form), False
expr = HyExpression([HySymbol(name), contents])
return imports, expr.replace(form), False

elif isinstance(form, HyCons):
ret = HyExpression([HySymbol(name)])
Expand Down
20 changes: 10 additions & 10 deletions hy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# license. See the LICENSE.

from __future__ import unicode_literals
import collections
from contextlib import contextmanager
from math import isnan, isinf
from hy._compat import PY3, str_type, bytes_type, long_type, string_types
Expand Down Expand Up @@ -62,9 +63,11 @@ def wrap_value(x):

wrapper = _wrappers.get(type(x))
if wrapper is None:
if (not isinstance(x, HyObject)
and isinstance(x, collections.Iterable)):
return wrap_value(list(x))
return x
else:
return wrapper(x)
return wrapper(x)


def replace_hy_obj(obj, other):
Expand Down Expand Up @@ -226,11 +229,8 @@ class HyList(HyObject, list):
"""

def replace(self, other):
for x in self:
replace_hy_obj(x, other)

HyObject.replace(self, other)
return self
self[:] = [replace_hy_obj(x, other) for x in self]
return HyObject.replace(self, other)

def __add__(self, other):
return self.__class__(super(HyList, self).__add__(other))
Expand Down Expand Up @@ -387,11 +387,11 @@ def __iter__(self):

def replace(self, other):
if self.car is not None:
replace_hy_obj(self.car, other)
self.car = replace_hy_obj(self.car, other)
if self.cdr is not None:
replace_hy_obj(self.cdr, other)
self.cdr = replace_hy_obj(self.cdr, other)

HyObject.replace(self, other)
return HyObject.replace(self, other)

def __repr__(self):
if PRETTY:
Expand Down
9 changes: 9 additions & 0 deletions tests/native_tests/quote.hy
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,12 @@
[1 2 3]
[4 5 6])
[4 5 6])))

(defn test-unquote-with-generators []
(defn values []
(yield '1)
(yield '2)
(yield '3))

(assert (= (eval `[~@(values)]) [1 2 3]))
(assert (= (eval `[~(values)]) [[1 2 3]])))