3 ms·
Well that (default being TCO, and explicitly declaring a stack call) would mean every normal function call (the vast majority) would need to be explicitly decla
by retrogradeorbit 10y ago
Well that (default being TCO, and explicitly declaring a stack call) would mean every normal function call (the vast majority) would need to be explicitly declared (call func args...), and the implicit case would need to blow up everywhere it's invoked in a non-tail position.
What I was getting at is: in an automatic TCO language you don't actually know if the call is optimised or not. You might think it's optimised, but it isn't, because it's not in the tail position. You don't find this out until one day in production the stack explodes. The only way to know if it's optimised is to determine if it is in the tail position. That sometimes is straight forwards, but sometimes requires careful thought. It is certainly not always obvious.
Additionally, another coder can come along and break your tail position. You've gone "return func()" and it's TCO and then later someone changes it to "return 1 + func()" and now the call to func is not TCO, but there is no warning and no obvious outward signs that this has changed.
In the explicit TCO of clojure, if the second programmer adds the extension the compiler will explode with something like "recur not in tail position", immediately informing you that this has happened, and then you may either refactor to put it back in the tail position, or change it to a full stack function call once you determine that building up stack is ok in this case.
Now I think loop/recur was implemented in Clojure as a work around the JVM limitations, but in doing so I think Hickey stumbled onto a really great new syntactic formulation for TCO. Explicitly declared TCO. I like it.
- paulddraper 10y agoThis is why I like Scala's approach so much. It will TCO automatically. If you want to make sure you haven't made a mistake, simply add the @tailrec annotation, and the compiler will give you an error if it can't TCO.
- retrogradeorbit 10y agoThat sounds pretty good! I will check that out.
- groovy2shoes 10y agoClojure's loop/recur is pretty much a degenerate version of Scheme's named let, which has been part of Scheme since long before Clojure was a thing: (define (sum xs) (let loop ((acc 0) (xs xs)) (if (null? xs) acc (loop (+ acc (car xs)) (cdr xs))))) It's often used in Scheme for iteration, including cases where you would otherwise need to define a helper function to implement an iterative (tail-recursive) version of a function, as I did with sum above. The named let construct is more general than loop/recur, since you can supply a name and properly nest the forms.