8000 Conditionals waste assignments. · Issue #842 · hylang/hy · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Conditionals waste assignments. #842
Open
@gilch

Description

@gilch

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 if form, instead of optimizing nested forms.
  • It make Hy's if act more like Python's cascading elif.
  • 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.
  • cond can be redefined in the same way, and get the same benefits.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0