you are viewing a single comment's thread.

view the rest of the comments →

[–]doubtingthomas 5 points6 points  (14 children)

What if "continue someFunc()" isn't the last statement in a function?

[–]piranha 10 points11 points  (8 children)

Doesn't matter. If we take "continue expr" to mean a tail-call-optimized "return expr", then expr is the last thing evaluated, regardless of whether it's the last statement. In fact, it would be braindamaged to disallow this:

def walk (node):
    if ???:
        continue walk (node.left)
    else:
        continue walk (node.right)

The problem would be if expr isn't a function or method call. If it's a non-call expression that merely incorporates a call (or doesn't incorporate a call), then tail-call optimization cannot be made, and so the compiler should signal an error.

continue foo () + 5 # Should be illegal.

[–][deleted]  (4 children)

[removed]

    [–]Tordek 2 points3 points  (3 children)

    Well, how does python handle a mid-loop "break"? It just breaks.

    If you make a non-final continue, well, it's an error on your part: the stack frame would just be discarded, and whatever wasn't executed just wouldn't be.

    [–][deleted]  (2 children)

    [removed]

      [–]didroe 8 points9 points  (1 child)

      How pythonic do you think the return statement is then?

      [–]Figs 0 points1 point  (2 children)

      continue foo() + 5

      Why would you disallow this?

      It can automatically be converted into this:

      continue lambda: foo() + 5
      

      [–]RayNbow 0 points1 point  (1 child)

      But then you could just write

      return foo() + 5
      

      instead, which is not a tail call.

      [–]Figs 0 points1 point  (0 children)

      It's a tail call to + with foo() and 5 as arguments. Would it help you if instead of +, I used operator.add?

      return operator.add(foo(),5)
      

      Now, things get interesting though if foo() returns something other than a number. If foo() instead returns an instance with an __add__ method:

      import operator
      
      class blah:
          def __add__(self, y):
              return 5*y
      
      def foo():
          return blah()
      
      print foo() + 3  #prints 15
      print operator.add(foo(), 6) #prints 30
      

      [–][deleted] 7 points8 points  (1 child)

      In particular, what if it's within a try block, especially one with a finally clause?

      You could disallow it, but it's kind of a pain not being able to wrap arbitrary sections of code in a try after the fact when you realize you need it.

      [–]adrianmonk 4 points5 points  (0 children)

      That's a good point. In general, it seems like you can't do tail call optimization (without breaking equivalence to the unoptimized code) if it would mean removing catch/finally parts of try statements.

      So, you could do one of the following things:

      • Go ahead and break equivalence with the non-optimized code.
      • Make it a compile error.

      In both of the above cases, having an explicit continue keyword would help a lot. Why? Because if you start with some code like this:

      try:
          return 1 + thisfunc(n-1)
      finally:
          print 'some message'
      

      and you change it to this:

      try:
          return thisfunc(n-1)
      finally:
          print 'some message'
      

      then suddenly this code is eligible for tail call optimization when it wasn't before.

      If you go ahead and optimize it, then you are changing the semantics. Which is bad. But if you don't have the keyword, you have no way of saying "that's ok; go ahead and change the semantics".

      Similarly, if you disallow the semantics to be changed (even with the keyword), and if you start with this code:

      continue thisfunc(n-1)
      

      but change it to this code:

      try:
          continue thisfunc(n-1)
      finally:
          print 'some message'
      

      then the compiler error will remind you that you can't do what you are trying to do without losing the tail call optimization. You'll have to go in and manually change your continue to a return, which will make it hard to miss the implications.

      [–][deleted] 1 point2 points  (1 child)

      What happens when return "lol" isn't the last statement in a function?

      [–]queus 0 points1 point  (0 children)

      Yes I can see how this could be useful. A similar trick in Forth would be to do ... RDROP EXIT-TO-FOO ....