PROG1-LET is a binding macro modeled closely after WHEN-LET and friends, which I have regularly found useful in code to implement the "create, modify, return" pattern common in some imperative code. As a simple and, I believe, widely useful macro, I'd like to see this enter into Alexandria proper. Docstring follows:
Creates new variable bindings and executes FORMS, returning the initial value of the first binding.
BINDINGS must be either single binding of the form:
(variable initial-form)
or a list of bindings of the form:
((variable initial-form) (variable-2 initial-form-2) ... (variable-n initial-form-n))
All initial-forms are executed sequentially in the specified order, then all the variables are bound to the corresponding values and FORMS are executed as an implicit PROGN. Finally, the value returned by INITIAL-FORM is returned.
On 7 April 2012 23:25, Benjamin Saunders ralith@gmail.com wrote:
PROG1-LET is a binding macro modeled closely after WHEN-LET and friends, which I have regularly found useful in code to implement the "create, modify, return" pattern common in some imperative code. As a simple and, I believe, widely useful macro, I'd like to see
Thanks!
Can you show an example or two of this pattern as you use it?
Cheers,
-- Nikodemus
On Sat, Apr 7, 2012 at 4:34 PM, Nikodemus Siivola < nikodemus@random-state.net> wrote:
On 7 April 2012 23:25, Benjamin Saunders ralith@gmail.com wrote:
PROG1-LET is a binding macro modeled closely after WHEN-LET and friends, which I have regularly found useful in code to implement the "create, modify, return" pattern common in some imperative code. As a simple and, I believe, widely useful macro, I'd like to see
Thanks!
Can you show an example or two of this pattern as you use it?
Cheers,
-- Nikodemus
I find metabang-bind useful for this kind of thing, http://common-lisp.net/project/metabang-bind/ (though it doesn't have the prog1 aspect as far as I know). Is it necessary to duplicate functionality in alexandria?
Liam
On Sun, Apr 08, 2012 at 04:22:07PM -0400, Liam Healy wrote:
Is it necessary to duplicate functionality in alexandria?
Though it's not my place to judge that, I thought it worth making the proposal on the basis that WHEN-LET and IF-LET provide very similar functionality, and are already in alexandria. I originally modeled PROG1-LET after these, and have since found it to be useful with reasonable frequency.
Notably, all of these constructs are simple and obvious in both behavior and expansion, and provide some functionality in addition to creating a binding. As far as I can tell, metabang-bind is not described by any of that.
Meant to send this yesterday, but I appear to have bungled that:
On Sat, Apr 07, 2012 at 11:34:44PM +0300, Nikodemus Siivola wrote:
Can you show an example or two of this pattern as you use it?
Certainly. I've been experimenting with a compiler backed by LLVM, and using its IR builder, I often wish to create a function, define its body, then return the function. This usually takes the form of:
(prog1-let (func (llvm:add-function ...)) (setf (llvm:linkage func) :internal (llvm:function-calling-convention func) :fast) (llvm:with-object (local-builder builder) (llvm:position-builder-at-end local-builder (llvm:append-basic-block func "entry")) (loop :for (form . rest) :on body :for genned := (codegen module local-builder form) :unless rest :do (llvm:build-ret local-builder genned))))
Or when generating code for an if statement, where I wish to create a phi instruction, specify its inputs, and then return it:
(prog1-let (phi (llvm:build-phi builder (llvm type) "result")) (llvm:add-incoming phi (list then-result else-result) (list then-block else-block)))
I also use it within the module system to allocate a module object, create the bindings implied by its import list, and return it.
Certainly. I've been experimenting with a compiler backed by LLVM, and using its IR builder, I often wish to create a function, define its body, then return the function. This usually takes the form of:
(prog1-let (func (llvm:add-function ...)) (setf (llvm:linkage func) :internal [...]
i'd use a custom macro here called (with-new-llvm-node ...) or somesuch, and that would also make the code a whole lot more easier on the brain when reading.
and if it's a regular and often repeated construct in the codebase, then i'd even introduce an implicit -node- variable in that macro (which is colored specially in my emacs).
IMHO.
On Sat 07 Apr 2012 10:25:00 PM CEST, Benjamin Saunders wrote:
PROG1-LET is a binding macro modeled closely after WHEN-LET and friends, which I have regularly found useful in code to implement the "create, modify, return" pattern common in some imperative code. As a simple and, I believe, widely useful macro, I'd like to see this enter into Alexandria proper. Docstring follows:
I usually use something like
(aprog1 (cons 'initially 0) (incf (cdr it)))
to implement this pattern using the ANAPHORA library. Your macro would of course be a better solution if, for some reason, one doesn't want to use an anaphoric macro, but IMO it would be cleaner to just handle a single form, eg
(defmacro prog1-let ((variable initial-form) &body body) `(let ((,variable ,initial-form)) (prog1 ,variable ,@body)))
because the first form is special anyway. A multiple-value extension that returns all values in the let form would be interesting:
(defmacro prog1-values-let (bindings &body body) ;; could be named better? `(let ,bindings (multiple-value-prog1 (values ,@(mapcar (compose #'car #'ensure-list) bindings)) ,@body)))
(prog1-values-let ((mycons (cons 'initially 0)) (mynum 9)) (incf (cdr mycons)) (decf mynum))
=> (initially . 1), 9
And similarly with LET*.
Best,
Tamas
On Sun, Apr 08, 2012 at 12:33:55PM +0200, Tamas K Papp wrote:
I usually ... implement this pattern using the ANAPHORA library. Your macro would of course be a better solution if, for some reason, one doesn't want to use an anaphoric macro
Yeah, that's the idea. As with all the other macros in bindings.lisp, it's just a cleaner version of the same functionalith ANAPHORA provides. I took the presence of when-let and if-let to imply that this was established as a reasonable thing for alexandria to do.
but IMO it would be cleaner to...
I agree that the multiple binding use-case is a bit weak, but opted to support it for the sake of consistency with the other *-let macros already in alexandria. Returning them as multiple values is an interesting way to develop that into something more useful. I'm not sure I can see that being taken advantage of often, but it doesn't detract from the common use case, so I see no reason not to support it.
PROG1-LET is a binding macro modeled closely after WHEN-LET and
my 0.02:
i don't like prog1-let, because it's not substantially shorter than the alternatives, not a very regular pattern in my experience, makes code less readable in cases where a simple aprog1 is not desirable, and it adds quite some complexity when reading the code.
again, it's a subjective opinion.
On Sun, Apr 08, 2012 at 06:24:33PM +0600, Attila Lendvai wrote:
it's not substantially shorter than the alternatives
True. However, the same can be said of every other macro in bindings.lisp, and this does not seem to have precluded their inclusion. It certainly does not preclude my glad use of them.
not a very regular pattern in my experience
Certainly this depends on the style of the code in which you are working. Many of my uses of it pertain to interacting with foreign libraries which are characteristically highly imperative, in ways that Lisp often is not. Nonetheless, interacting with such libraries is something I do often, and I do not expect I am alone in that.
makes code less readable in cases where a simple aprog1 is not desirable
I'm not entirely sure what cases you're referring to. As a matter of style, I prefer not to use macros which implicitly bind names, and therefore make use of constructs of the sort found in bindings.lisp in place of anaphora. Thus I would argue that aprog1 is in general never desirable.
In cases where aprog1 is objectively unusable, such as nested forms, then this would seem to be nearly equivalent, except that the explicitly named binding allows unambiguous reference to be made, which is hardly less readable.
and it adds quite some complexity when reading the code.
This is indeed subjective. I find it to be clearer to read than the alternatives, but perhaps that's just me? Anaphora would seem to be widely used for a reason, though, and this is but a minor adjustment to that same concept...
On 9 April 2012 00:05, Benjamin Saunders ralith@gmail.com wrote:
and it adds quite some complexity when reading the code.
This is indeed subjective. I find it to be clearer to read than the alternatives, but perhaps that's just me? Anaphora would seem to be
WHEN-LET and IF-LET have a fairly common use-case:
(when-let ((x (gethash key table))) (bar x))
compared to
(let ((x (gethash key table))) (when x (bar x)))
where in a typical case the /relative/ reduction in lines of code is a substantial 33% -- and they are also "classic": been around forever, re-invented independently by several people. For the single-binding case there is also little chance of "guessing wrong" what it actually does.
In case of PROG1-LET, I actually assumed
(prog1-let ((x (foo))) (bar x) (quux x))
would have been equivalent to
(let ((x (foo))) (prog1 (bar x) (quux x)))
and the possibility it might mean
(let ((x (foo))) (prog1 x (bar) (quux)))
didn't even register -- in particular because I would write that as just a LET, without using PROG1 at all:
(let ((x (foo))) (bar x) (quux x) x)
-- or better yet, when possible make QUUX return X so the return value would be implicit:
(let ((x (foo))) (bar x) (quux-and-return x))
at which point we're back at the same line count as PROG1-LET, and have easier to read code.
A "bind values, do stuff, return the bindings" -macro similar PROG1-LET would IMO fit fine in Alexandria, but PROG1-LET is not a good name for it. I also suspect it only gains true utility if it returns multiple values, but then we're back at looking for use-cases...
Cheers,
-- Nikodemus
On Tue, Apr 10, 2012 at 10:13:33AM +0300, Nikodemus Siivola wrote:
WHEN-LET and IF-LET have a fairly common use-case ... where in a typical case the /relative/ reduction in lines of code is a substantial 33% -- and they are also "classic": been around forever, re-invented independently by several people.
Though I'm no judge of classicality, judging from the response on this list the anaphoric form, aprog1, is fairly well known and used. It certainly isn't much shorter than the obvious approach with let, but I find it to be clearer to read: while let is often used for intermediate values of only local interest, prog1 clearly expresses that the value in question is to be returned by the form, without requiring that every possible codepath within the form be considered.
For the single-binding case there is also little chance of "guessing wrong" what it actually does. In case of PROG1-LET, I actually assumed...
This surprises me. WHEN-LET and IF-LET establish a pattern of "bind a value and pass it as the first argument of the corresponding form," which generalizes directly to my implementation of PROG1-LET. On this basis I had assumed that the expansion was so obvious as to barely merit explanation beyond that implied by the name itself.
in particular because I would write that as just a LET, without using PROG1 at all
Perhaps my assumptions are skewed due to my familiarity with this pattern's nearly equivalent incarnation in aprog1, which I have used to the same end in the past, but I don't seem to be unusual in that regard.
A "bind values, do stuff, return the bindings" -macro similar PROG1-LET would IMO fit fine in Alexandria, but PROG1-LET is not a good name for it.
I'm glad to hear you approve of the general idea. I'm certainly not attached to the name--the entire PROG class of names has always struck me as a gratuitously non-obvious throwback--and only named it in that manner for consistency and assumed obviousness-of-function in the presence of the existing binding macros. Given that I seem to have overestimated this effect, that rationale breaks down. I have named similar abstractions 'returning' in the past, though 'returning-let' seems a bit unwieldly. Do any superior names occur to you?
I also suspect it only gains true utility if it returns multiple values, but then we're back at looking for use-cases...
As previously mentioned, I find this macro useful primarily not to make code more concise, but more obvious of meaning, and feel that its existence is adequately justified by that alone. That said, it would hardly be impaired by returning multiple values and thereby reducing boilerplate in the (perhaps uncommon) use-case of a binding multiple-value-prog1.
On 10 April 2012 21:42, Benjamin Saunders ralith@gmail.com wrote:
Though I'm no judge of classicality, judging from the response on this list the anaphoric form, aprog1, is fairly well known and used. It
Despite having written ANAPHORA, I actually never use anything but AWHEN, and very rarely AIF -- and even those only in codebases where they pre-exist. I don't find them terrible an bad, but I don't really like them.
That said,
(aprog1 (foo) (bar it))
is pretty unambiguous. It's clear what is being returned, and that is also the only thing that makes sense as the value of IT. I don't find this to be true for PROG1-LET.
seems a bit unwieldly. Do any superior names occur to you?
No, sorry. If I had one up my sleeve I would have owned up. :)
LET-RETURN begs the question which block it is going to return from.
LET-VALUES historically and typically refers to something that expands into MULTIPLE-VALUE-BIND.
Nothing obvious occurs to me. Meh, names are /hard/, I'll make some tea instead. :)
Cheers,
-- Nikodemus
alexandria-devel@common-lisp.net