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

Replace apply with unpacking operators #1325

Merged
merged 6 commits into from
Jul 20, 2017
Merged
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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ Changes from 0.13.0

[ Language Changes ]
* `yield-from` is no longer supported under Python 2
* `apply` has been replaced with Python-style unpacking operators `#*` and
`#**` (e.g., `(f #* args #** kwargs)`)
* Single-character "sharp macros" changed to "tag macros", which can have
longer names
* Periods are no longer allowed in keywords
Expand Down
5 changes: 3 additions & 2 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import _pytest
import hy
from hy._compat import PY3
from hy._compat import PY3, PY35

def pytest_collect_file(parent, path):
if (path.ext == ".hy"
and "/tests/native_tests/" in path.dirname + "/"
and path.basename != "__init__.hy"
and not ("py3_only" in path.basename and not PY3)):
and not ("py3_only" in path.basename and not PY3)
and not ("py35_only" in path.basename and not PY35)):
m = _pytest.python.pytest_pycollect_makemodule(path, parent)
# Spoof the module name to avoid hitting an assertion in pytest.
m.name = m.name[:-len(".hy")] + ".py"
Expand Down
96 changes: 52 additions & 44 deletions docs/language/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,41 +154,6 @@ it appends it as the last argument. The following code demonstrates this:
5 10


apply
-----

``apply`` is used to apply an optional list of arguments and an
optional dictionary of kwargs to a function. The symbol mangling
transformations will be applied to all keys in the dictionary of
kwargs, provided the dictionary and its keys are defined in-place.

Usage: ``(apply fn-name [args] [kwargs])``

Examples:

.. code-block:: clj

(defn thunk []
"hy there")

(apply thunk)
;=> "hy there"

(defn total-purchase [price amount &optional [fees 1.05] [vat 1.1]]
(* price amount fees vat))

(apply total-purchase [10 15])
;=> 173.25

(apply total-purchase [10 15] {"vat" 1.05})
;=> 165.375

(apply total-purchase [] {"price" 10 "amount" 15 "vat" 1.05})
;=> 165.375

(apply total-purchase [] {:price 10 :amount 15 :vat 1.05})
;=> 165.375

and
---

Expand Down Expand Up @@ -596,8 +561,8 @@ Parameters may have the following keywords in front of them:
parameter_1 1
parameter_2 2

; to avoid the mangling of '-' to '_', use apply:
=> (apply print-parameters [] {"parameter-1" 1 "parameter-2" 2})
; to avoid the mangling of '-' to '_', use unpacking:
=> (print-parameters #** {"parameter-1" 1 "parameter-2" 2})
parameter-1 1
parameter-2 2

Expand Down Expand Up @@ -634,19 +599,19 @@ Parameters may have the following keywords in front of them:

.. code-block:: clj

=> (defn compare [a b &kwonly keyfn [reverse false]]
=> (defn compare [a b &kwonly keyfn [reverse False]]
... (setv result (keyfn a b))
... (if (not reverse)
... result
... (- result)))
=> (apply compare ["lisp" "python"]
... {"keyfn" (fn [x y]
... (reduce - (map (fn [s] (ord (first s))) [x y])))})
=> (compare "lisp" "python"
... :keyfn (fn [x y]
... (reduce - (map (fn [s] (ord (first s))) [x y]))))
-4
=> (apply compare ["lisp" "python"]
... {"keyfn" (fn [x y]
=> (compare "lisp" "python"
... :keyfn (fn [x y]
... (reduce - (map (fn [s] (ord (first s))) [x y])))
... "reverse" True})
... :reverse True)
4

.. code-block:: python
Expand Down Expand Up @@ -1576,6 +1541,49 @@ the given conditional is ``False``. The following shows the expansion of this ma
(do statement))


unpack-iterable, unpack-mapping
-------------------------------

``unpack-iterable`` and ``unpack-mapping`` allow an iterable or mapping
object (respectively) to provide positional or keywords arguments
(respectively) to a function.

.. code-block:: clj

=> (defn f [a b c d] [a b c d])
=> (f (unpack-iterable [1 2]) (unpack-mapping {"c" 3 "d" 4}))
[1, 2, 3, 4]

``unpack-iterable`` is usually written with the shorthand ``#*``, and
``unpack-mapping`` with ``#**``.

.. code-block:: clj

=> (f #* [1 2] #** {"c" 3 "d" 4})
[1, 2, 3, 4]

With Python 3, you can unpack in an assignment list (:pep:`3132`).

.. code-block:: clj

=> (setv [a #* b c] [1 2 3 4 5])
=> [a b c]
[1, [2, 3, 4], 5]

With Python 3.5 or greater, unpacking is allowed in more contexts than just
function calls, and you can unpack more than once in the same expression
(:pep:`448`).

.. code-block:: clj

=> [#* [1 2] #* [3 4]]
[1, 2, 3, 4]
=> {#** {1 2} #** {3 4}}
{1: 2, 3: 4}
=> (f #* [1] #* [2] #** {"c" 3} #** {"d" 4})
[1, 2, 3, 4]


unquote
-------

Expand Down
4 changes: 2 additions & 2 deletions docs/language/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1216,9 +1216,9 @@ if *from-file* ends before a complete expression can be parsed.

=> (import io)
=> (def buffer (io.StringIO "(+ 2 2)\n(- 2 1)"))
=> (eval (apply read [] {"from_file" buffer}))
=> (eval (read :from_file buffer))
4
=> (eval (apply read [] {"from_file" buffer}))
=> (eval (read :from_file buffer))
1

=> ; assuming "example.hy" contains:
Expand Down
19 changes: 5 additions & 14 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -423,30 +423,21 @@ The same thing in Hy::
=> (optional-arg 1 2 3 4)
[1 2 3 4]

If you're running a version of Hy past 0.10.1 (eg, git master),
there's also a nice new keyword argument syntax::
You can call keyword arguments like this::

=> (optional-arg :keyword1 1
... :pos2 2
... :pos1 3
... :keyword2 4)
[3, 2, 1, 4]

Otherwise, you can always use `apply`. But what's `apply`?

Are you familiar with passing in `*args` and `**kwargs` in Python?::

>>> args = [1 2]
>>> kwargs = {"keyword2": 3
... "keyword1": 4}
>>> optional_arg(*args, **kwargs)

We can reproduce this with `apply`::
You can unpack arguments with the syntax ``#* args`` and ``#** kwargs``,
similar to `*args` and `**kwargs` in Python::

=> (setv args [1 2])
=> (setv kwargs {"keyword2" 3
... "keyword1" 4})
=> (apply optional-arg args kwargs)
=> (optional-arg #* args #** kwargs)
[1, 2, 4, 3]

There's also a dictionary-style keyword arguments construction that
Expand All @@ -460,7 +451,7 @@ looks like:
The difference here is that since it's a dictionary, you can't rely on
any specific ordering to the arguments.

Hy also supports ``*args`` and ``**kwargs``. In Python::
Hy also supports ``*args`` and ``**kwargs`` in parameter lists. In Python::

def some_func(foo, bar, *args, **kwargs):
import pprint
Expand Down
Loading