Earlier quoted context omitted.
Implementing exception handling (with cleanup on unwind) on top of setjmp was not uncommon. GCC still does on some targets. But I'm sure you know this more than I do. I think that RAII is different from unwind-protect and other scope based cleanup (finally, defer, with) as it is tied to object lifetimes. The fact that automatic objects lifetimes are tied to scope is a nice feature, but RAII goes beyond that.
Well lisps don’t really have destructors in the sense C++ does; objects go out of scope and become unreachable. So unwind-protect makes cleanup (closing a file, for example) explicit, which it has to be. I wouldn’t say RAII is “tied to object lifetime” except in its name; at least I think of it as every {} pair defining an unwind-protect with object creation/destruction being how it is effected by the programmer. Per…
Common Lisp's block / return-from and unwind-protect
41–50 of 63 posts
Re: Common Lisp's block / return-from and unwind-protect
#42Earlier quoted context omitted.
Thanks for pointing to MDL: http://www.ifarchive.org/if-archive/infocom/info/MDL_Primer_... Nevertheless, the MDL UNWIND and the later UNWIND-PROTECT are more limited in applications and they require much more work from the programmer than the mechanism introduced by Stroustrup in 1980. With implicitly-invoked destructors, the destructor body is written once for each type of data, and normally there is no need to eve…
UNWIND-PROTECT is a building block. Resource management in the MIT Lisp OS ca. 1980, approximate example. One would define a resource of arrays, where arrays can be allocated and deallocated. They will be managed via a pool. (defresource 2d-array (rows columns) :constructor (make-array (list rows columns))) Now user code would use the USING-RESOURCE macro, where it spans a dynamic scope. Entering the scope allocates…
I agree that this is pretty much equivalent to RAII.
Nevertheless, it is also obvious that this was not a source of inspiration for Bjarne Stroustrup.
He has started directly from the constructors and destructors of C.A.R. Hoare (1965-11) and Simula 67 (1968-05, Kristen Nygaard & Ole-Johan Dahl).
The only change is that in 1980 he has enhanced his compiler for "C with Classes" to generate automatically all the invocations to the appropriate destructors in all the block and function epilogues, relieving the programmer from this task.
Re: Common Lisp's block / return-from and unwind-protect
#43Earlier quoted context omitted.
Well lisps don’t really have destructors in the sense C++ does; objects go out of scope and become unreachable. So unwind-protect makes cleanup (closing a file, for example) explicit, which it has to be. I wouldn’t say RAII is “tied to object lifetime” except in its name; at least I think of it as every {} pair defining an unwind-protect with object creation/destruction being how it is effected by the programmer. Per…
What I mean is that if I add an instance of a class with a non trivial destructor from a container and later remove it, the destructor is automatically invoked. This is different from an unwind protect.
For example WITH-OPEN-FILE closes the file automatically when leaving its scope.
(with-open-file (stream "hello.world")
(read stream))
WITH-OPEN-FILE is a macro and expands into OPEN and CLOSE operations, protected by an UNWIND-PROTECT.Thus anything where a destructor would automatically clean things up is done in Lisp behind the scenes automatically using an UNWIND-PROTECT form.
Re: Common Lisp's block / return-from and unwind-protect
#44Earlier quoted context omitted.
Well lisps don’t really have destructors in the sense C++ does; objects go out of scope and become unreachable. So unwind-protect makes cleanup (closing a file, for example) explicit, which it has to be. I wouldn’t say RAII is “tied to object lifetime” except in its name; at least I think of it as every {} pair defining an unwind-protect with object creation/destruction being how it is effected by the programmer. Per…
What I mean is that if I add an instance of a class with a non trivial destructor from a container and later remove it, the destructor is automatically invoked. This is different from an unwind protect.
This is a low-level memory management strategy being conflated with object-orientation, that is alien to higher level languages.
Re: Common Lisp's block / return-from and unwind-protect
#45Earlier quoted context omitted.
Thanks for pointing to MDL: http://www.ifarchive.org/if-archive/infocom/info/MDL_Primer_... Nevertheless, the MDL UNWIND and the later UNWIND-PROTECT are more limited in applications and they require much more work from the programmer than the mechanism introduced by Stroustrup in 1980. With implicitly-invoked destructors, the destructor body is written once for each type of data, and normally there is no need to eve…
UNWIND-PROTECT is a building block. Resource management in the MIT Lisp OS ca. 1980, approximate example. One would define a resource of arrays, where arrays can be allocated and deallocated. They will be managed via a pool. (defresource 2d-array (rows columns) :constructor (make-array (list rows columns))) Now user code would use the USING-RESOURCE macro, where it spans a dynamic scope. Entering the scope allocates…
Re: Common Lisp's block / return-from and unwind-protect
#46Earlier quoted context omitted.
What I mean is that if I add an instance of a class with a non trivial destructor from a container and later remove it, the destructor is automatically invoked. This is different from an unwind protect.
The object is not exactly removed from the container. It is removed from existence. The object in that situation resides in the container physically. It is constructed there and destroyed. It does not exist outside of the container afterwards, or at any other time. This is a low-level memory management strategy being conflated with object-orientation, that is alien to higher level languages.
Yet I think an high level language could have the same value/reference distinction. Or you could do it with linear types.
Re: Common Lisp's block / return-from and unwind-protect
#47Earlier quoted context omitted.
What I mean is that if I add an instance of a class with a non trivial destructor from a container and later remove it, the destructor is automatically invoked. This is different from an unwind protect.
Lisp uses UNWIND-PROTECT to implement that. For example WITH-OPEN-FILE closes the file automatically when leaving its scope. (with-open-file (stream "hello.world") (read stream)) WITH-OPEN-FILE is a macro and expands into OPEN and CLOSE operations, protected by an UNWIND-PROTECT. Thus anything where a destructor would automatically clean things up is done in Lisp behind the scenes automatically using an UNWIND-PROTEC…
But the essence of RAII is more than that.
In c++ if I add an opened file object to some collection and later remove it or destroy the collection, it is implicitly cleaned up.
For example let's say you are implementing an n-way out of core merge: in c++ you would create a priority queue of files objects ordered by the front current from item. You pop the front file object read the item and, if the file is not empty, push it back into the queue. Cleanup is implicit by removing the file from the queue and not explicitly adding it back. On early exit (because of an error or exception) the queue is automatically destroyed and recursively all the file objects.
There might be a way to implement this recursively with unwind-protect, but I think it is less natural.
For example python has ExitStack, which in practice is an ad-hoc container that supports recursive cleanup of contained objects, but is still not as convenient as having all containers do proper cleanup.
Re: Common Lisp's block / return-from and unwind-protect
#48Earlier quoted context omitted.
Longjmp in standard C, swapcontext in POSIX C. I think GCC allows gotos to the containing function from a local function (but I never used GCC local functions). Smalltalk, and I believe ruby, allow non local return from blocks.
Smalltalk only allows the return from the block's lexically enclosing method, not an arbitrarily specifiable context like Common Lisp does. It's discussed a bit on c2.com [1] [1] https://wiki.c2.com/?SmalltalkBlockReturn
Re: Common Lisp's block / return-from and unwind-protect
#49Earlier quoted context omitted.
+1 nice summary! I have been using Common Lisp since the early 1980s and I also love the language. For my own Lisp hacking pleasure, I add a few Schemes to the mix, but that is often a (fun) distraction for me, and I would be better off sticking with CL (or Python for deep learning projects). Do you have a blog where you share your learning experiences?
OMG! This is what I'm talking about with Common Lisp. One minute you're a washed-up slacker, the next Mark L. Watson speaks to you. I'm winding my way through "Loving Common Lisp, or the Savvy Programmer's Secret Weapon" as we speak and loving it. Thank you! I don't have a blog and haven't gotten far enough in my learning to make anything useful, but I do plan to produce a newbie's guide to help people get started qu…
Re: Common Lisp's block / return-from and unwind-protect
#50Earlier quoted context omitted.
I'm learning programming at age fifty-four and have chosen Common Lisp as my one language for life. If you want something easy to learn with a ton of support pick Python, Elixir, and/or JS. CL sucks for mini-projects since the learning curve requires serious commitment and is SO different from what people are used to. CL requires a long-term commitment and demands the programmer challenge pop programming trends and d…
Hello my friend, I am very pleased to make your acquaintance and I am very excited about your journey. I am also very impressed that for someone who is newly learning the field, you already are so well-spoken and knowledgeable about the language and the tools. Thank you so much for your response. I also appreciate that you are only a little older than my parents, and if you are learning so well and so quickly then th…
Emacs is the weakest link in the Common Lisp experience but, yes, you have to defeat that boss to move up. I love/hate Emacs and mostly struggle to do anything. GNU Emacs is a ball of mud with decades of technical debt in dire need of a ground-up re-imagining. Emacs Lisp made a lot of inexplicable choices but it is a Lisp and Lisp is why Emacs survives. RMS' stewardship has been consistent but underwhelming. He's no Jose Valim. The editing model is inadequate, as shown by Kakoune and structural editors. And yet, Emacs is alive and malleable while so many other editors/IDEs are inorganic artifacts that start to rust as soon as they are released. We do have the nascent Alive for VSCode and VIM options, but they are not really improvements over Emacs, nor are they likely to be. VSCode is the latest CodeWarrior and will suffer the same fate. It's only propped up by daddy's money.
Doom Emacs tooling, package management, and sensible defaults take much of the pain out of Emacs. Sly, the LSP-like Emacs mode for CL, is too much fun and gives a hint of what Lisp Machine development must have been like. Remote editing and daemon mode are so practical. Once you've painstakingly hammered out a config and workflow, Emacs starts to hum along pleasantly. But getting there is relentlessly frustrating and trying something new sends you right back to the treadmill. Once I figure out why Lem SDL2 (an Emacs clone in Common Lisp) isn't building for me, I'll attempt a switch.
I have daily fantasies of jumping back to my high school graduation in 1986 with everything I know now. I'd put up sexy posters of Guy Steele and Alan Kay in my dorm room. The only real advantage of starting programming at my age is that I know what I want and why. I'm following the humanistic thread of computing from Paul Otlet -> Vannevar Bush -> Doug Englebart -> Ted Nelson -> Bret Victor. But I read the same chapters over and over and still haven't written any code beyond homework exercises. The task feels Sisyphean and my trauma-addled, post-alcoholic brain struggles to stay focused. My only consolation is that I can't think of anything better to do with my remaining time and the Common Lisp community is good company. I love being around eccentric people who are much smarter than me.
Python is an eminently practical, perfectly respectable choice. So naturally, my life goal is to kill it. In my head, Symbolics was open sourced after the AI winter and grew to become the obvious choice for most tasks. I wish that people wrote new languages as DSLs on top of Common Lisp rather than littering GitHub with vanity projects that die on the vine. Think of all that wasted effort--like turning up the thermostat with the front door open.
When people ask about the choice of Common Lisp or Scheme, I ask which Scheme? No two are alike and none of them has achieved enough momentum to be truly practical. You have to pick one and pray your requirements don't exceed it. In CL, we invest in implementations--ABCL, ECL, Clasp--each one practical, each one mostly runs your code unchanged. Our spec is ancient by today's standards so old code just works. We innovate in our package ecosystem which gives users of whichever implementation access to fresh work. Compare to the whitewater churn of the NodeJS ecosystem. I have a Ghost blog that's a few years old and I can't update it as things have changed so much there is no practical way.
I'd like to file off the most common pain points in CL, spackle over obvious gaps in functionality, improve documentation, and polish the developer experience until nothing stands between Lisp and world domination. It's the once and future language. I have no qualifications for the job whatsoever except resentment of the status quo, a burning need for elegance, and lack of employment. It's a short resume, but the nice thing about being self-appointed is that no one has to want to hire you.