Yea, that was my #2 consideration. I wasn’t talking specifically of Clojure. I meant in general, people moved away from goto, because it was eventually demonstrated to be unnecessary given a good compiler and people found the non goto using code to be simpler.
You’re right, when it comes to Clojure, mutual recursion will add boxing into thunks, and require an extra trampoline. But, I would try it before calling it slower. The JDK is quite an advanced compiler, it might still end up faster then the CL implementation you are mimicking.
It is halfway between a goto and a loop. Should allow equal performance, but you might need to rewrite the algo to suit it.
You could also, if you are adventurous, try to use core.async, I believe csp can mimick goto behaviour. Now the implementarion might be trickier.
You can use the clojure.asm namespace to compile directly to bytecode. This gives you full control, but at that point, you are almost hand coding in the JDK bytecode, which is close to assembly. There’s also tools.emitter.jvm, and insn which allow for similar control, but are not baked into clojure core.
Yes, me too. I just prefaced my post, because there’s obviously going to be differences in opinion here, and it’s not like anyone can say one opinion is better than the other.
Ya, I agree. I think I just like to distinguish things more granularity as well as more generally.
For example, I think a def is imperative, in that it is a global write to memory, it isn’t a procedure or a function, but almost a pure instruction directly to memory or cpu.
A do I feel is more challenging. It can be used imperatively, for example:
(def a 20)
(def b 30)
(if (> a b)
(def b "Hello World!")
(def b "Bye bye World!"))
But I wasn’t free to use whatever I wanted inside the do, or in my opinion, it would have already started to become procedural. For example:
(let [a 20 b 30]
(if (> a b)
(println "Hello World!")
(println "Bye bye World!"))))
This is now procedural code to me. I introduced local variables, and the idea of subroutines (the nesting of println inside if).
What do isn’t is functional. Or used functionally it is redundant and can be removed.
So I say, imperative is a series of instructions direct to cpu and memory. Even my use of if is pushing it in my opinion. Normally it would be a conditional jump to the instructions that defs b, but Clojure doesn’t have goto, so I had to cheat a bit.
Procedural is a set of instructions where instructions can be grouped inside of subroutines, aka, procedures, which can be nested in one another, and call each other, where execution automatically returns back to where we were and values can be passed in and out of them at known jump points. This adds the idea of scoped data, a move away from global data, and new instructions to manipulate that local data.
Functional makes away completly with global data, and with series of instructions. There are no instructions to cpu, or access to global memory. The entire program is a tree of functions composed together, which gets reduced using term re-writing into the final result:
(println (#(if (> a b) "Hello World!" "Bye bye world!") 20 30))
This is cheating as well, because println is an instruction, but since the computer only executes instructions, you need some somewhere to be useful, so normally in functional it is pushed to the edge like I did.
And where you contrast imperative and declarative. I think I don’t associate functional with declarative per say. It isn’t imperative for sure. But declarative to me is more orthogonal. Imperative is when you say “How to do something, but not what”. And declarative is when you say “What to do, but not how”, and the computer figures out how automatically. For example, html is like that, or Apache Ant, yet those are not functional languages, but they are declarative. Logic programming is another example. You could say ML is declarative as well. Sometimes, FP is said to be more declarative, but that’s more to do with the available libraries and features common to FP langs then FP itself I feel. Like higher level functions, like filter, remove, partition, etc., are closer to What then How. But you could have an FP lang without them. Or monadic let is more declarative in that you say What, and the computer figures out how to order the things for it to work. But you can have an FP lang without monadic let.
Edit: Oh, and I had kind of lost my train of thought. So above is my granular differentiation. In practice, borders are blurred. That’s when I switch to a more general taxonomy. The essence of Clojure is functional. It tries to be as much as is practical. So in that way, it isn’t an imperative or procedural language. Just like a SUV isn’t a truck, even though I can use it to move a TV around, or I can attach a UHAUL to it. That’s because the essence of an SUV is different to that of a truck. Yet, it is still a car, with an above average cargo size, and a potentially more powerful motor, which could have higher clearance, etc.
@seancorfield, correct me if I’m wrong, but recur does not really give tail call semantics; it only allows looping. Right? For example I cannot use recur to make two different functions tail call each other. I can only use it to emulate a function calling itself in tail position. As I understand, I cannot even use recur to have a local function call its parent function in tail position.
Correct, you’d need to use trampoline there if you didn’t want actual recursive calls.
Correct, recur is specifically for self-call tail recursion.
In reality, there is a lot less recursion in Clojure than you might think. reduce, transducers, and other higher-level constructs are the idiomatic way to deal with a lot of things where you might use recursion in CL, for example.
We have a 90,000 line code base and the perceived limitations you are asking about are not a concern in real-world code, in my experience.
I think it’s important to understand that Clojure != Lisp in the classic sense (even tho’ it is in the Lisp family). Clojure takes a different, and very carefully designed, path based on being a high-level but general purpose JVM-based language, first and foremost. That means that its idioms are sometimes different from other Lisps, as well as to other functional languages – and it is very strongly influenced by the JVM being its host in certain places.
Perhaps I am repeating someone else’s comment. Apologies if so.
I think the short answer is that no, Clojure does not have an arbitrary goto capability. If you want that, you can write Java code and use its goto. Java is effectively Clojure’s “in-line assembler” language, except it is not in-line.
It seems that leaving out an arbitrary goto construct was an explicit design choice, not an accident.
What to do in the situations you have described where it might be nice to have it? You could use Java. You could use C, compile it, and then JNI or JNA to link that code into a JVM and call that from Java or from Clojure. But it seems unlikely to me that the Clojure maintainers believe they are missing the boat of a bunch of capabilities that they want in the language by not having goto. The use cases you describe are unlikely to be theirs, or else they would have added it.
There is a bytecode for it, and it is a reserved keyword in Java source code so they could add it to Java later if they wanted to, without breaking existing Java programs that might have otherwise used ‘goto’ as an identifier name. But it has never been added to Java.
So it looks like if you want to use the JVM goto byte code, you must generate JVM byte code yourself (which a few libraries enable you to do, including one used by Clojure’s compiler itself), or find another JVM-based language that issues such goto JVM opcodes. I do not know of any, but I haven’t looked, either.
Like James Gosling for Java (and Java language developers since him), Rich Hickey chose not to include goto in Clojure.
FWIW, I’m developing an embedded DSL library right now to make it easier to write imperative (and efficient) code in Clojure.
JiSE compiles Clojure-flavored Java code (or Java-flavored Clojure code?) into JVM bytecode at macro expansion time. It’s still far from complete, but is already equipped with some convenient features to write imperative code, such as assignment, nested loops, labeled break/continue, etc. Also, I’m considering adding Common Lisp’s tagbody-like construct as an extension for future work.
In CL (tagbody) and (go) are not really a goto like a setjmp/longjmp C style branching but is implemented in CL…
From an implementation standpoint, if you’re interpreting a Lisp-like program, you might do something a bit like this:
Upon entering a tagbody , begin a table of destinations. (a map of symbol→list after tag)
Iterate each form within the tagbody
if (symbolp this-element) , then store the address (a pointer to that form) into the table
otherwise, (eval this-element) as usual
When encountering a go form, look up the destination symbol, and (destructively) change your program’s “current instruction” pointer to that value (in our case recur to loop). Then, jump to your routine to fetch the next instruction.
When exiting the tagbody , just discard the destination table.
The destination tables will (ultimately) need to be a stack (referred-to in older Lisp documentation as a “push-down list” or PDL), since you’ll search upwards through dynamic scope to find the tag in question. Keep in mind, in Common Lisp, go tags are a separate namespace from variables, functions, classes, et al.
You can write your tagbody/go functions in Clojure probably in a Macro/function with a loop/recur to manage the go. Excellent exercise trying to make it as pure function…
But anyway tagbody/go was not meant to create spaghetti pseudo FORTRAN IV- or BASIC code, but to create looping macros for the CL language…
@leonoel wrote it, but if I understand correctly, the execution is always falling/flowing through, imperatively moving down like a do block. And location is reset with a go, so your example would I guess print “hello world\n” repeatedly and point-a is just ignored after the first loop.
That’s a bug. Thanks for pointing it, and see the updated version. The overall strategy is the same : split the body blocks around labels, and wrap blocks into functions returning the block to run next. Then trampoline this in a loop and catch exceptions thrown by go to emulate jumps.
BTW do clojure macros support local macros? I.e., could the tagbody implementation provide a local macro named go which only has scope within the tagbody. If so, that go could expand to some kind of call to recur.