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

Add reader macro for generator expressions #867

Closed
gilch opened this issue Jul 30, 2015 · 8 comments · Fixed by #1985
Closed

Add reader macro for generator expressions #867

gilch opened this issue Jul 30, 2015 · 8 comments · Fixed by #1985
Labels

Comments

@gilch
Copy link
Member

gilch commented Jul 30, 2015

For Python generator expressions, the parentheses can be omitted on calls with only one argument:

tuple((x for x in range(12) if x%2==0))
# same as
tuple(x for x in range(12) if x%2==0)

This happens pretty often in Python. tuple is just a simple example.

Hy has no such shortcut:

(tuple (genexpr x [x (range 12)] (even? x)))

Since we seem eager to add syntactic sugar if Python has it (e.g. #860 #829), what do you guys think of adding something similar for generator expressions?

We could come close with a reader macro:

(defreader G [expr]
  `(~(car expr) (genexpr ~@(cdr expr))))

#G(tuple x [x (range 12)] (even? x))

I'm not really sure if 'G' (for Generator expression) is the appropriate character. Perhaps we want to reserve the alphabet for end users?

Alternatively, perhaps we could use an ordinary macro?

(gencall tuple x [x (range 12)] (even? x))

Not sure if this is better.

@paultag
Copy link
Member

paultag commented Jul 30, 2015

This is some proper hackery :)

Interesting! I'm not huge on #G myself, but I love the idea, and I'm sure we can bikeshed the byte we put there cc/ @hylang/core

@algernon
Copy link
Member

How about this?

(defreader G [expr]
  `(genexpr ~expr))

(tuple #G(x [x (range 12)] (even? x)))

Yes, I know, no parens/braces saved. But still shorter... Can't put my finger on why, but #G(tuple x [x (range 12)] (even? x)) feels strange. Mind you, I'm just throwing around an idea now.

The reason the "alias"-like reader macro feels better to me is that in Python, the syntax makes the generator expression simpler. So does the macro. If we put the #G outside of the whole form, I don't know... that suggests we apply it to the whole expression, which is not exactly the case.

@gilch
Copy link
Member Author

gilch commented Jul 31, 2015

The character that says "generator expression" to a Python programmer isn't G, but (. I'd hoped to hear some more opinions before running with this, but assuming we go with @algernon 's plan of a reader macro alias for genexpr, can we just use #() instead? I think it's possible because we did #{} for sets.

I know this dashes any hope of Hy eventually using that syntax for an equivalent of Clojure's #(%) lambdas, but might I suggest #%(%1) instead for that purpose? (In Hy, unlike Clojure, the % can't be an alias for %1, because it's already the Python % operator.) The #% will be easy to remember, since the implicit parameters start with %.

If we do create a reader macro alias for genexpr there's no reason we couldn't implement the ordinary gencall macro as well.

@algernon
Copy link
Member

#() sounds better, indeed. I faintly remember trying to name a reader macro that, and failing (because the parens get parsed or something earlier). So that may need some changes in deeper parts of Hy, too.

@Kodiologist
Copy link
Member

@gilch Do you still want this, considering that the new name for genexpr, gfor, is pretty short?

@Kodiologist
Copy link
Member

Admittedly, gfor doesn't save any parens.

@gilch
Copy link
Member Author

gilch commented Dec 3, 2019

I don't think gfor changes much here. I'm not sure how much Hy really needs this, but passing a generator as the sole argument is a common pattern in Python.

An updated approach might be cfor (callable-for) which takes a callable expression as its first argument:

(cfor tuple
  x (range 12)
  :if (even? x)
  x)

This could be a macro in terms of gfor, similar to the earlier gencall idea, but with the new lfor-style clauses. This means that e.g. (cfor list ... is equivalent to (lfor ... in the same way Python's list(x for x in foo) is like a listcomp [x for x in foo].

And, of course, this could be a tag macro instead.

#for(tuple x (range 12)
           :if (even? x)
      x)

Note that we're not restricted to a single-character tag now. I think #for is more readable than the earlier #G idea.

@Kodiologist
Copy link
Member

That seems pretty reasonable, yeah.

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

Successfully merging a pull request may close this issue.

4 participants