-
Notifications
You must be signed in to change notification settings - Fork 372
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
Make and/or short-circuit #824
Conversation
While I agree with the general sentiment that some behaviors around hy's "turn all statements into expressions" stance are weird, I'm a bit uneasy with the approach of wrapping everything into function calls. See for instance |
(also, I have no clue for a "more proper" way of fixing this :/) |
I wonder if we can constrain what we find in the or/and as conceptually part of the same scope and cheat a little. |
e.g. actually getting aggressive with |
@olasd Well, it doesn't wrap everything. Just when one of the sides is a statement. Of course, if Python 2 had nonlocal, I could just do what @paultag said and aggressively nonlocal everything. IMO, the issue with things as they stand is when macros get involved. This:
could be bad if |
Wrapping statements into a function call might not be the right approach, because of the aforementioned scoping issues. Why not make Hy use a macro like this instead? (defmacro -and [&rest args]
(cond [(empty? args) True]
[(cdr args) `(if ~(car args)(-and ~@(cdr args)))]
[True (car args)])) Some tests with => (-and)
True
True
=> (-and 0 (assert 0))
if 0:
assert 0
_hy_anon_var_1 = None
else:
_hy_anon_var_1 = None
=> (for [i (range 9)] (-and (= i 2)(break))(print i))
from hy.core.language import range
for i in range(9):
if (i == 2):
break
_hy_anon_var_1 = None
else:
_hy_anon_var_1 = None
print(i)
0
1 Hy doesn't appear to introduce any unexpected scope issues this way. As with the proposed function wrapping, Hy need only use the macro when the arguments to |
@gilch I can try updating the PR with that. |
(assert (= and-false False))) | ||
; short circuiting | ||
(setv a 1) | ||
(and 1 (setv a 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be (and 0 (setv a 2))
to short circuit?
@paultag @gilch @olasd All complaints/issues fixed! Short-circuiting operations are now compiled into
|
When looking over this commit I noticed |
@gilch Done. :) (Note that the apparent presence of 3 commits is just GitHub getting confused by a rebase; I referenced this PR instead of the issue you opened.) |
Hm. If I'm reading this right, ;;; R5RS
> (or)
#f I realize that interactive Python suppresses printing >>>None
>>>False
False
>>> But now that we have a On the other hand, this might be a good argument for making ;;; Clojure
user=> (if false 'truthy 'falsey)
falsey
user=> (if nil 'truthy 'falsey)
falsey
user=> (if 0 'truthy 'falsey)
truthy |
@gilch I prefer using both Of course, if anyone objects, feel free to mention it. :) |
Then why not change user=> (or)
nil * (or)
NIL Common Lisp doesn't even have a false, just Another special case to check: user=> (and nil)
nil
user=> (and false)
false |
@Glich You're right; I said it backwards. I meant that having Then again, every other Lisp does this, so I should probably just use |
@gilch Fixed. |
@kirbyfan64 Looks pretty good, though that compile function is getting long-winded. One minor nitpick: the hy2py output could be prettier if it compiled using I would like to get this merged, but it's not really up to me. |
@gilch I produce the same AST as an if a: b
elif c: d is the exact same thing as: if a: b
else:
if c: d Then again, I tested it with Python 2. Does Python 3 have an explicit node for |
Dunno, lemme check: Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> from ast import dump,parse
>>> w_elif = dump(parse("""
if x:
pass
elif y:
pass
else:
pass
"""))
>>> no_elif = dump(parse("""
if x:
pass
else:
if y:
pass
else:
pass
"""))
>>> w_elif
"Module(body=[If(test=Name(id='x', ctx=Load()), body=[Pass()], orelse=[If(test=Name(id='y', ctx=Load()), body=[Pass()], orelse=[Pass()])])])"
>>> no_elif
"Module(body=[If(test=Name(id='x', ctx=Load()), body=[Pass()], orelse=[If(test=Name(id='y', ctx=Load()), body=[Pass()], orelse=[Pass()])])])"
>>> w_elif == no_elif
True
>>> I think it doesn't. Sounds like a separate issue then. I wonder if this can be fixed easily in the pretty printer. I have no further objections. I can haz merged? |
If you can make astor codegen fail, please add a test, and then we'll add a fix. See berkerpeksag/astor#46 Thanks, P.S. I just double-checked, and the code to print 'elif' was in the code from Armin Ronacher that I started with back in late 2012... |
Wait, it does print
We just established that the AST should be the same as if I had used an @kirbyfan64 can you show us the |
How can you print an elif if you have those additional lines? (e.g. _hy_anon_var_3 = _hy_anon_var_2 in the second example?) |
You're right, of course. I see it now. There couldn't have been an |
In the case of the I think Hy could have done this instead with the same effect:
Presumably, the current version was easier to implement. It does result in some wasted assignments though. |
@kirbyfan64 @olasd @paultag Are we good to merge this now, or do you guys want to deal with #842 first? Even if the AST is a little ugly at this point, it does seem to short-circuit statements as expected, which fixes a pretty serious problem. We can clean it up later. |
@hylang/core RFR |
If there are no more objections, I can squash this now. |
I have one more suggestion, perhaps for later, and more related to #842. Even in the case that |
For example: (print (and 1 2 3 (setv x 1) 4 5 6))
_hy_anon_var_1 = 1 and 2 and 3
if _hy_anon_var_1:
x = 1
_hy_anon_var_1 = x and 4 and 5 and 6
print(_hy_anon_var_1) |
I'd say that's a different issue @gilch. A worthwhile topic to explore, but indeed, more related to #842, so lets track that separately from this. Can you open a bug? @kirbyfan64 Squash it! :) |
Uhhh...I tried to squash it...and I think I messed something up... I love Git. But I hate it. Is this even mergeable now? |
@kirbyfan64 join IRC tomorrow and i'll help you fix it \o/ |
basically
or try revert the changes you did with rebase (not likely) and try again |
@Foxboron It worked!!!! Thank you!!! I'll remember The exact commands I ran were:
Now it's ready! |
\o\ \o/ /o/ |
This might be a little late to the party, but: Mathematically, + and 'or' are similar and * and 'and', i.e.
whereas
In formal boolean algebraic ring, this is even how the + and * operators are defined. In math, an empty product is usually defined as a 1, and an empty sum is usually a 0. This is why it makes sense for an empty or to return 0 and an empty and to return 1. |
👍 |
This basically wraps statements into a function call. It only wraps statements, so
(and 1 1)
will still be compiled to just1 and 1
.Closes #233 and #766.