> It would be nice if one could annotate (and thus make the compiler verify) tail calls.
This always comes up, but honestly, I've never really run into the problem where I mistook a non-tail call for a tail call. (And I've programmed the majority of my career in languages like Haskell, OCaml and Erlang.)
Of course, I'm always in favour of the compiler verifying more stuff. It just doesn't seem very pressing.
I've not yet heavily used macros in my project, but surely have used macros others wrote, sometimes perhaps even without knowing, aside from the Scheme implementations standard forms. However, I have yet to hit a case, where I thought something was a tail call, but in the end was not. It just does not seem to come up for me and has never been a problem.
Perhaps I have only used "good macros" or something, which do not make seemingly tail calls into non-tail calls?
is it possible you actually did get a non tail call where you thought it would be one, but just didn't go deep enough to blow up the stack so you weren't aware? It could be possible the call recursed "only" 10,000 times and didn't blow up any stack.
Or perhaps you've only used good macros. But it is incredibly easy for a macro to end up with (+ 0 x) where you think it will end up with "x".
It's also possible that you have used a macro that ended with (+ 0 x), the optimizer turned it into "x" and you got the tail call eliminated but were not guaranteed to get that, and it may blow up on another Scheme implementation.
That's what I don't like about it; The compiler definitely knows if it did TCE or cannot, and it does change program semantics if it did or didn't. So why can't I just annotate the code saying "well, I intend this to TCE, please complain if you can't?"
I see your point about being explicit. I think ideally the specification would be precise enough for the user to conclude: "If this is a specification conforming Scheme implementation, then this must be a tail call." I was under the impression, that this is the case with the Scheme specification, but I might be wrong, as I am not very knowledgeable in terms of the specification. If it is not so specific about what exactly becomes a tail call, then I would say, that it would be great to have an explicit way to tell the interpreter or compiler, that you want something to definitely be a tail call.
... and if you have that declaration mechanism, then it can easily work for any call in any position whatsoever!!!
So that further separates the concept "tail call" from "call in tail position".
Any position can be the tail position if we just wave he right magic wand at it, so the concept becomes meaningless. All we are left with is the style of call: returning versus goto-like.
Well, in Scheme you need to rely on TCE, if you want your code to run for more than a specific finite time. There are no other looping constructs.
Haskell does have several macro systems, but they are not nearly as pervasively used as in Lisp languages. (Mostly, because laziness makes it easier to add eg something as foreign as even eg Prolog as an embedded DSL without macros.
And, of course, adding to Haskell syntax feels less natural than in Lisp.)
This always comes up, but honestly, I've never really run into the problem where I mistook a non-tail call for a tail call. (And I've programmed the majority of my career in languages like Haskell, OCaml and Erlang.)
Of course, I'm always in favour of the compiler verifying more stuff. It just doesn't seem very pressing.