4 ms·
> Check. Lisp provides garbage collection, but you don't have to use it. It's perfectly possible to write Lisp programs that do manual memory management. It isn
by Jweb_Guru 13y ago
> Check. Lisp provides garbage collection, but you don't have to use it. It's perfectly possible to write Lisp programs that do manual memory management. It isn't often done because it's hardly ever a win, but if you really want to you can.
How does LISP work without a garbage collector? Closures without a garbage collector are pretty awful. Let's keep in mind that Rust gives us a pretty good idea of what a safe system without a GC looks like, and it doesn't look anything like LISP.
- lisper 13y ago> How does LISP work without a garbage collector? The same way any other language works without one: you allocate the storage you need and manage it yourself. It's not pretty, but it can be done. The resulting code ends up looking an awful lot like the code in any other imperative language. (Math gets a little tricky because you have to be careful not to inadvertently create bignums, but other than that it's pretty straightforward.) > Closures without a garbage collector are pretty awful. > Rust gives us a pretty good idea of what a safe system without a GC looks like And yet, Rust has closures :-) (And indeed, they are pretty awful.) Writing non-consing code in Lisp is no different from writing non-consing code in any other language. You can produce stack-allocated closures that get cleaned-up on function return, just as in Rust. If you want to write non-consing code in Lisp (or any other language) you just can't use first-class closures.
- Jweb_Guru 13y agoRight, I guess to me it's just almost not worth using a LISP if I can't use any of the features that make LISP enjoyable to use (LISP without conses, closures or any other interesting features). But I suppose in principle a very carefully written imperative LISP program could be pretty fast, sure :) I'd rather write in a language that supports that style of programming natively in that eventuality, though. (I'd also add that there are some issues surrounding larger unboxed types, but I won't venture to posit how SBCL handles those).
- lisper 13y agoYou can have conses, you just have to allocate them all up-front. The when you want one, you don't call CONS, you call MY-CONS, which grabs one of the pre-allocated conses off your free list. Then when you're done with it, you push it back onto your free list so it can be reused. It's no different from having MALLOC and FREE, except that you have to write them yourself (but that's not hard). But yes, it's a lot easier to write code with a GC than without one. That's true in any language. But let's not forget that the original article was saying, essentially, "Hey, look, we can make C++ do Lispy things!". My point is just that if you want to do Lispy things it's a lot easier just to use Lisp than to try to shoehorn Lisp's features into C++.
- Jweb_Guru 13y agoIt isn't really, though. If you write your own malloc and free like that (by the way, writing a performant, bugfree, concurrent malloc and free is not that easy :)), you're responsible for safety as well (e.g. use after free bugs) which LISP will no longer protect you against. That's not to mention that LISP has to interact with C on a regular basis for things like system calls, and comes with a runtime that prevents it from playing nicely as an embedded library (perhaps SBCL has a way of running without one, but I can't find it... and that's not really a product of functional-ness or lack of static compilation either either, I have heard from several people who have trouble using libraries built with ghc or Go). And in embedded contexts, you may need hard guarantees that, for example, no dynamic allocation of any sort is done, or that your program doesn't use the stack, etc. To the best of my knowledge, LISP has no facilities for either of these things. (It does appear that SBCL lets you drop down to assembly, but again if you do that all the advantages of using LISP are gone. Anyway, what is the goal here? Do you really want to use a typed LISP with no lists, with large of featureless statically allocated memory, manually handling concurrency, mutability everywhere, inline assembly, and the inability to use even most of the C++ LISPy features because LISP has no support for using them without runtime costs? Writing a language without resorting to costly abstractions is hard and it was explicitly never a goal of LISP to be one. That's not to mention that in LISP it's nonobvious which features are costly and which ones aren't, so the abstraction it provides over hardware is only theoretical in this context). In any LISP in a high performance context, you are always paying for things you don't use. You could probably argue that some of the above problems could be mitigated if everyone adopted SBCL as the standard, but unfortunately that's just the way it is in the real world. And while it is unfortunate, the fact is that even all the technical problems could be resolved (I have my doubts), it would be much more irritating to write such low-level systems code in LISP than in a language that wasn't so far removed from the workings of modern computer architecture. I'm actually a big fan of Lisp and I've found it quite useful for a number of projects, but when you really need to do low-level programming, it is significantly easier in (modern) C++.