I think that "industrial get-your-boss-off-your-back-oriented development works" and "Complex work" is a false dichotomy.
I think that "industrial get-your-boss-off-your-back-oriented development works" and "Complex work" is a false dichotomy.
It's because of these abstractions that we can predict the motions of planets or build computers.
This also explains the "why" of functional programming. Referentially transparent functions are just simpler than methods that are constantly changing. Of course, in the real world, it's neither desirable nor possible to eliminate mutable state: you sequester and manage it.
The opposite of mathematical rigor (defined semantics, reproducible behavior) is the sloppy, ad-hoc, corporate FactoryVisitor stuff that is tolerated under the assumption that programming has nothing to do with math.
Less sarcastically, the mere existence of the builder pattern in Java, which undermines referential transparency to its core, is proof that the design of Java as both a language and community is strongly biased against a functional, referentially transparent, mathematical approach to programming, even if such a style were theoretically possible[1].
[1] eg. com.sun.java.swing.plaf.nimbus.InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
[2] Scala and Clojure being proof that it is still possible (though only to a lesser extent - tail call elimination and corecursion being an example where it fails completely).
In clojure, loop..recur and trampoline does the job. Tail recursion is not automatic, but still, it's there and quite straight forward to use with the added benefit of recur cribbing if your call isn't a tail call.
You may know this already, but my understanding is that Clojure opts to not automatically eliminate tail calls in order to use Java calling conventions, which I imagine simplifies or eases interop. I'm not defending this decision, as I don't have much of an opinion on it, but to call it a design flaw or a bug seems a bit disingenuous as there is definitely a tradeoff to be made.
Now, while Clojure just not implementing automatic TCO would not be a bug or a design flaw per se -- clisp does fairly well without it, because the developers just didn't bother -- if implementing such an AST transformation in the Clojure compiler is impossible, then the compiler's design is faulty. It's like programming FizzBuzz so that you can't later modify it to add a case for multiples of 7. OK, yeah, TCO without goto is not such a piece of cake, but you get the idea.
And I don't think it's a trade-off in the least. If so, what are Clojure programmers winning now? The glorious overhead of thousands of function calls? Those tasty "stack overflow" back-traces, with thousands of calls to the same function?
Rich Hickey wrote:
"No language that uses the JVM stack and calling convention (e.g. Clojure and Scala) can do TCO since it would require either stack manipulation capabilities not offered in the bytecode spec or direct support in the JVM. The latter is the best hope for functional languages on the JVM, but I'm not sure is a priority for Sun as tail-calls are not idiomatic for JRuby/Jython/ Groovy." [0]
I thought that there are implementations of Scheme and other languages on the JVM that do tail call optimization, but I assumed they utilised some kind of virtual stack. Jörg W Mittag on StackOverflow confirmed this:
"Nah, TCO [on the JVM] is easy. Seph does it, Erjang does it, Kawa and all the other Scheme implementations on the JVM do it. The JVM has Exceptions, which are basically the same as GOTO, which can be used to implement TCO. Or you use trampolines. Or you don't use the JVM call stack at all and just implement your own. The reason why Clojure and Scala only provide limited TCO (basically, only tail recursion is optimized) is because they want to use the JVM call stack for interoperability and performance reasons. As Rich Hickey, Clojure's designer said: Interop, speed, TCO -- Pick two." [1]
James Iry wrote the following on LtU, which explains why even though "Real instruction sets (or C) don't 'support' proper tail recursion either", the fact that the JVM doesn't is an issue for Clojure (and Scala):
"Native instruction sets often let you do whatever you want with the stack. C doesn't in the ANSI standard, but you can do it with a bit of assembly. .NET IL has an explicit instruction for tail calls. The JVM, on the other hand, is very strict about how you use its stack and has no tail call instruction." [2]
During my Googling, I also found this[3], which is pretty interesting!
"CTCO works by applying a first-order one-pass CPS algorithm (via Danvy 2007), then transforming the code to return thunks, and finally creating a custom trampoline to be used when the code is executed. Thanks to the properties of the CPS transformation, CTCO will make all function calls into tail calls, thereby even making non-tail code compiled by CTCO use constant space."
This actually sounds not far from what you suggested! Although it does seem to be a bit more than "just an AST transformation". I haven't read through it, or read the referenced paper [4] it's a bit over my head. You should check it out and report back! I've bookmarked it for now.
Finally, you wrote:
"And I don't think it's a trade-off in the least. If so, what are Clojure programmers winning now? The glorious overhead of thousands of function calls? Those tasty 'stack overflow' back-traces, with thousands of calls to the same function?"
Do you still think it's not a trade-off? Unless that last link I found is a magic bullet that makes TCO fast without interfering with Java interop (or the performance of Java interop) then I really don't think it's fair to say that it's not a design trade-off. Rich Hickey (and Martin Odersky) obviously made a conscious decision about this, and you're welcome to disagree with them, but it's not clear cut.
What Clojure programmers are winning now (by being hosted on the JVM) is a lot, I think. Easy interop is one of the pillars of Clojure that has contributed heavily to it picking up steam. I think the same applies to Scala. I said earlier that I don't have an opinion, and I guess it's only true that I don't have a strong opinion. If I had to pick, I think I'd pick interop.
Sorry for the long comment but I'd already done the research, I figured I might as well share it, although there really already has been a great deal said about this.
[0] https://groups.google.com/forum/?fromgroups=#!msg/clojure/Oi...
[1] http://stackoverflow.com/questions/7261039/haskell-on-jvm
[2] http://lambda-the-ultimate.org/node/3106
[3] https://github.com/cjfrisz/clojure-tco
[4] http://www.brics.dk/RS/07/Abs/BRICS-RS-07-Abs/BRICS-RS-07-Ab...
If you were writing a virtual guestbook for a company which has many factories, you would have an excellent reason for having objects with names like FactoryVisitor.
Philosophers of mathematics spent the 20th century what mathematics is all about and they did not settled on any potential definition.
Ok, I have to disagree with you here. What Godel established is that there's no complete axiom system. Every mathematical formalism will have statements P for which neither P nor ~P can be proven. This was not an unexpected result. What makes Godel's Incompleteness Theorem awesome is that it proved incompleteness, which few people thought possible.
Same with Turing and the Halting Problem. Almost no one actually believed that such a program (that could determine, algorithmically, if a program halted) existed. If one did, it would blow open all of mathematics. Turing proved, in a very elegant way, that it didn't.
What 20th-century mathematicians agree upon is that mathematical statements aren't "true" or "false" in an absolute, platonic sense, but that they are products of the axiom systems that generate them.
Whether the Axiom of Choice or Continuum Hypothesis are "true" is meaningless. These aren't mathematical "controversies" that have people yelling at each other saying that the other is wrong. They're axioms about infinity (specifically, uncountable infinity) that, although they have no physical correlates (you can't actually Banach-Tarski an orange) are logically independent of the "obvious" axioms. What logically independent means is that neither L nor ~L will generate a contradiction, and therefore neither has any absolute high ground.
Most mathematicians use AoC and CH for typical mathematics, but there are alternative mathematical worlds in which they don't hold, and those are interesting in their own right.
AoC is used indirectly in almost every place of pure mathematics, but I researched numerical methods for PDEs and Stochastic PDEs which is not pure mathematics, maybe someone who is much more intelligent than me will say where you will use AoC directly in this area outside of some theorems of Analysis that are used but well, I've never ever touched this same axiom again in my life and if I chose to spend my life as a researcher I doubt I will ever need it again to work and publish, and yet I was able to get a Ph.d in Applied Mathematics. The theorem that I know uses the AoC that I cited once but not exactly used is Banach–Alaoglu theorem.
As sbi put if you go to a department of mathematics that includes mathematicians, statisticians and applied mathematicians chances are that almost no one will know too much of set theory excluding some pure mathematicians, this was true on almost every department that I saw in my entire life. So there are mathematicians whose life are not spent trying to use abstractions everywhere. That was the point.