-
Notifications
You must be signed in to change notification settings - Fork 375
Description
Conditionals containing statements are doing unnecessary work. I know, Python is not renowned for its performance, but let's not make it worse. This kind of thing can matter inside nested loops.
Currently a cond like this:
(cond [0 "zero"]
[1 "one"]
[True (assert 0)])
Turns into this:
if 0:
_hy_anon_var_3 = 'zero'
else:
if 1:
_hy_anon_var_2 = 'one'
else:
if True:
assert 0
_hy_anon_var_1 = None
else:
_hy_anon_var_1 = None
_hy_anon_var_2 = _hy_anon_var_1
_hy_anon_var_3 = _hy_anon_var_2
But wouldn't it would work just as well like this?
if 0:
_hy_anon_var_1 = 'zero'
elif 1:
_hy_anon_var_1 = 'one'
elif True:
assert 0
_hy_anon_var_1 = None
Half the assignments for the same effect. And the hy --spy output is much more readable. I don't think CPython can automatically optimize this kind of thing. I know not all of them happen for any given branch, but the deeper ones will still do more assignments than they should.
Python has no switch/case. Sometimes you can get away with dictionary lookups instead, but if you need side effects or performance, you're supposed to use cascading elif where you would have used a switch.
Currently there's no elif form in Hy. cond is the closest we've got. Now I could write nested ifs instead (or a macro to do it for me) but look at what it turns into:
=> (if 0 "zero"
... (if 1 "one"
... (assert 0)))
if 0:
_hy_anon_var_2 = 'zero'
else:
if 1:
_hy_anon_var_1 = 'one'
else:
assert 0
_hy_anon_var_1 = None
_hy_anon_var_2 = _hy_anon_var_1
Not quite as bad as the cond since we have a final else, but still twice what is necessary:
if 0:
_hy_anon_var_1 = 'zero'
elif 1:
_hy_anon_var_1 = 'one'
else:
assert 0
_hy_anon_var_1 = None
Maybe it's too hard to optimize nested if forms like that? Is cond defined in terms of Hy's if form in the first place? I didn't implement cond but you could totally do it as a macro in terms of if.
Why not extend Hy's if to work like Arc Lisp's (as I proposed in #830 ), which can accept two or more arguments:
=> (if a b)
if a:
_hy_anon_var_1 = b
else:
_hy_anon_var_1 = None
=> (if a b
c)
if a:
_hy_anon_var_1 = b
else:
_hy_anon_var_1 = c
=> (if a b
c d)
if a:
_hy_anon_var_1 = b
elif c:
_hy_anon_var_1 = d
else:
_hy_anon_var_1 = None
=> (if a b
c d
e)
if a:
_hy_anon_var_1 = b
elif c:
_hy_anon_var_1 = d
else:
_hy_anon_var_1 = e
etc., for any number of inner elifs you need.
- Now you only have to generate AST for the new
ifform, instead of optimizing nested forms. - It make Hy's
ifact more like Python's cascadingelif. - This doesn't break existing Hy code, since we're not using more than 3 arguments now, and the 2 and 3 argument cases work exactly like before.
condcan be redefined in the same way, and get the same benefits.