Benjamin Saunders | 7 Apr 2012 22:25
Picon

[PATCH] PROG1-LET

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.
From cbccc782f34477c924ea8ff7b6000cd52511a768 Mon Sep 17 00:00:00 2001
From: Benjamin Saunders <ralith <at> gmail.com>
Date: Sat, 7 Apr 2012 13:19:19 -0700
Subject: [PATCH] Added PROG1-LET
(Continue reading)

Nikodemus Siivola | 7 Apr 2012 22:34
Gravatar

Re: [PATCH] PROG1-LET

On 7 April 2012 23:25, Benjamin Saunders <ralith <at> 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

_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Liam Healy | 8 Apr 2012 22:22
Picon

Re: [PATCH] PROG1-LET

On Sat, Apr 7, 2012 at 4:34 PM, Nikodemus Siivola <nikodemus <at> random-state.net> wrote:
On 7 April 2012 23:25, Benjamin Saunders <ralith <at> 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
_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Benjamin Saunders | 8 Apr 2012 22:49
Picon

Re: [PATCH] PROG1-LET

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.
_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Benjamin Saunders | 8 Apr 2012 22:40
Picon

Re: [PATCH] PROG1-LET

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.
_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Attila Lendvai | 12 Apr 2012 09:53
Picon
Gravatar

Re: [PATCH] PROG1-LET

> 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.

--

-- 
 attila

Notice the erosion of your (digital) freedom, and do something about it!

PGP: 2FA1 A9DC 9C1E BA25 A59C  963F 5D5F 45C7 DFCD 0A39
OTR XMPP: 8647EEAC EA30FEEF E1B55146 573E52EE 21B1FF06

_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Tamas K Papp | 8 Apr 2012 12:33
Picon

Re: [PATCH] PROG1-LET

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
       , <at> 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 , <at> (mapcar (compose #'car #'ensure-list)
                                             bindings))
       , <at> body)))

(prog1-values-let ((mycons (cons 'initially 0))
                   (mynum 9))
  (incf (cdr mycons))
  (decf mynum))

      => (initially . 1), 9

And similarly with LET*.

Best,

Tamas
Benjamin Saunders | 8 Apr 2012 22:36
Picon

Re: [PATCH] PROG1-LET

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.
_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Attila Lendvai | 8 Apr 2012 14:24
Picon
Gravatar

Re: [PATCH] PROG1-LET

> 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.

--

-- 
 attila

Notice the erosion of your (digital) freedom, and do something about it!

PGP: 2FA1 A9DC 9C1E BA25 A59C  963F 5D5F 45C7 DFCD 0A39
OTR XMPP: 8647EEAC EA30FEEF E1B55146 573E52EE 21B1FF06

_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Benjamin Saunders | 8 Apr 2012 23:05
Picon

Re: [PATCH] PROG1-LET

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...
_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Nikodemus Siivola | 10 Apr 2012 09:13
Gravatar

Re: [PATCH] PROG1-LET

On 9 April 2012 00:05, Benjamin Saunders <ralith <at> 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

_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Benjamin Saunders | 10 Apr 2012 20:42
Picon

Re: [PATCH] PROG1-LET

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.
_______________________________________________
alexandria-devel mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel
Nikodemus Siivola | 10 Apr 2012 21:32
Gravatar

Re: [PATCH] PROG1-LET

On 10 April 2012 21:42, Benjamin Saunders <ralith <at> 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 mailing list
alexandria-devel <at> common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel

Gmane