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

apply not good enough to replace Python's unpacking #891

Closed
gilch opened this issue Aug 12, 2015 · 8 comments
Closed

apply not good enough to replace Python's unpacking #891

gilch opened this issue Aug 12, 2015 · 8 comments

Comments

@gilch
Copy link
Member

gilch commented Aug 12, 2015

In both Common Lisp and Python, I know that I can apply some parameters and then unpack the rest from a list.

Python.

>>> def foo(*args):
    print(args)


>>> foo(1,2,3,*[4,5,6])
(1, 2, 3, 4, 5, 6)

Here's a similar example in Common Lisp.

* (defvar foo (lambda (&rest args) (print args)))

FOO
* (apply foo 1 2 3 '(4 5 6))

(1 2 3 4 5 6)
(1 2 3 4 5 6)

Let's try that in Hy

=> (defn foo [&rest args] (print args))
=> (apply foo 1 2 3 [4 5 6])
  File "<input>", line 1, column 1

  (apply foo 1 2 3 [4 5 6])
  ^-----------------------^
HyTypeError: `apply' needs at most 3 arguments, got 5

=>

Hmnope. Hy's apply won't accept arguments before the list. Can we get a fix for this please?

@paultag
Copy link
Member

paultag commented Aug 12, 2015

apply takes up to 3 arguments - callable, args and kwargs.

 (apply foo (+ [1 2 3] [4 5 6]))  ; works

@paultag
Copy link
Member

paultag commented Aug 12, 2015

in particular, CL isn't dealing with what we have to deal with with **kwargs (same with Clojure by not having kwargs)

@gilch
Copy link
Member Author

gilch commented Aug 12, 2015

=> (defn foo [&rest args] (print args))
=> (apply foo (+ [1 2 3][4 5 6]))  ; works
(1, 2, 3, 4, 5, 6)
=> (apply foo (+ [1 2 3](, 4 5 6)))  ; fail
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
=> (apply foo (+ [1 2 3](genexpr x [x [4 5 6]])))  ; fail
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: can only concatenate list (not "generator") to list
>>> def foo(*args):
    print(args)


>>> foo(1,2,3,*[4,5,6])  # works
(1, 2, 3, 4, 5, 6)
>>> foo(1,2,3,*(4,5,6))  # works
(1, 2, 3, 4, 5, 6)
>>> foo(1,2,3,*(x for x in [4,5,6]))  # works
(1, 2, 3, 4, 5, 6)

+ no help enough.
apply not good enough.

Yeah, the kwargs make it more difficult than in other Lisps, but just a little bit. I still think we can do better than what we've got.

@gilch
Copy link
Member Author

gilch commented Aug 12, 2015

Here's option one:

(apply foo 1 2 3 :* (, 4 5 6) :** {"this is key" 42})
;; They're optional
(apply foo 1 2 3 :** {"this is key" 42})
(apply foo 1 2 3 :* (, 4 5 6))
(apply foo 1 2 3)
;; the other order would also work
(apply foo 1 2 3 :** {"this is key" 42} :* (, 4 5 6))

In this version, the args and kwargs are optional keyword-only arguments to apply. The keywords (:* and :**) are easy to remember if you're familiar with Python. I think I like this one best.

Here's option two:

(apply foo 1 2 3 (, 4 5 6) {})
(apply foo 1 2 3 (,) {})

In this version, the last two arguments are always the args and kwargs, but they can be empty if not needed. Just like how an apply works like a funcall in Common Lisp, when you give it a nil list at the end. apply would thus have a minimum of three arguments.

Option three.

(apply foo [1 2 3] (, 4 5 6) {"k" "v"})

Here, apply can have a maximum of four arguments. The one, two, and three argument versions behave as they do now. This has the advantage of not breaking code, but it's also my least favorite option. When you want to add arguments in the front, they go in square brackets, but you still have to pass it an iterable and dictionary, even if they're empty, as in option two.

@algernon algernon added this to the Grand Language Cleanup milestone Aug 12, 2015
@algernon
Copy link
Member

Slapped a Grand Language Cleanup milestone on this one.

@paultag
Copy link
Member

paultag commented Aug 12, 2015

Short of adding vectors around args and kwargs, I don't know how to do this
in a clean and predictable way.

And if + isn't good enough when you mix types, try itertools.chain.
On Aug 12, 2015 2:38 AM, "Gergely Nagy" notifications@github.com wrote:

Slapped a Grand Language Cleanup milestone on this one.


Reply to this email directly or view it on GitHub
#891 (comment).

@gilch
Copy link
Member Author

gilch commented Aug 12, 2015

Short of adding vectors around args and kwargs

Something like this?

(apply foo 1 2 3 [(, 4 5 6) {"k" "v"}])

You'd still need an empty args if you only wanted to unpack kwargs.

(apply foo 1 2 3 [(,) {"k" "v"}])

This doesn't seem any better than option two.

I don't know how to do this in a clean and predictable way.

Option three might be a little confusing, but the other two are very predictable.

And if + isn't good enough when you mix types, try itertools.chain.

chain does work. Still kind of a pain to need it and to import it.

@paultag
Copy link
Member

paultag commented Aug 12, 2015

No, I mean (apply [[1 2](, 3 r)])
On Aug 12, 2015 11:16 AM, "Matthew Egan Odendahl" notifications@github.com
wrote:

Short of adding vectors around args and kwargs

Something like this?

(apply foo 1 2 3 [(, 4 5 6) {"k" "v"}])

You'd still need an empty args if you only wanted to unpack kwargs.

(apply foo 1 2 3 [(,) {"k" "v"}])

This doesn't seem any better than option two.

I don't know how to do this in a clean and predictable way.
Option three might be a little confusing, but the other two are very
predictable.

And if + isn't good enough when you mix types, try itertools.chain.

chain does work. Still kind of a pain to need it and to import it.


Reply to this email directly or view it on GitHub
#891 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants