all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Lynn Winebarger <owinebar@gmail.com>
To: Philip Kaludercic <philipk@posteo.net>,
	Stefan Monnier <monnier@iro.umontreal.ca>
Cc: help-gnu-emacs@gnu.org
Subject: Re: inline function expansion
Date: Thu, 18 May 2023 10:56:39 -0400	[thread overview]
Message-ID: <CAM=F=bADJuA5TNgL_mxZrv7XMvcQUKVF1Y6AaABehpnz=9CmJQ@mail.gmail.com> (raw)
In-Reply-To: <CAM=F=bAsVyhMN2JEaqsExBb9_G4c52ABoXqX5m+-_WtrBDHnYQ@mail.gmail.com>

On Thu, May 11, 2023 at 3:11 AM Lynn Winebarger <owinebar@gmail.com> wrote:
> On Sun, May 7, 2023 at 3:48 PM Philip Kaludercic <philipk@posteo.net> wrote:
> > Lynn Winebarger <owinebar@gmail.com> writes:
> > > If I use define-inline, I would like to be able to verify that the
> > > result is what I expect, or vice versa, that I understand what the
> > > result will be well enough to have the correct expectation.
> >
> > Isn't the idea of inlining that the behaviour/effect of invoking a
> > function shouldn't change, just that the resulting code might be more
> > efficient?

In some languages, that is the definition.  But it can also be
interleaved with compile-time evaluation of constant-expressions to
provide a more structured alternative to defmacro.  At least, that's
how I read the motivation for define-inline versus defsubst.

The current implementation appears to be difficult to use in a
meaningful way - as per the below, I've only identified two cases that
make actual use of the facilities provided by define-inline beyond
those afforded by defsubst, and defsubst is much easier to use.  I've
been trying to work out how to provide the functionality define-inline
seeks to provide in a more convenient form without writing a
full-blown partial-evaluator.

> I was working off of the description in "Evolution of Emacs Lisp",
> page 45, which gives the example of cl-type-p for evaluation of
> constant expressions at compile-time via inline functions (figure 2):
> (define-inline cl-typep (val type)
>   (inline-letevals (val)
>     (pcase (inline-const-val type)
>       (`(not ,ty)
>        (inline-quote (not (cl-typep ,val ',ty))))
>       (`(eql ,v)
>        (inline-quote (eql ,val ',v)))
>       (`(satisfies ,pred) (inline-quote (funcall #',pred ,val)))
>       ((and (pred symbolp) ty (guard (get ty 'cl-deftype-satisfies)))
>        (inline-quote (funcall #',(get ty 'cl-deftype-satisfies) ,val)))
>       ...
>      (ty (error "Bad type spec: %s" ty)))))
>
> The info documentation does not include any examples involving
> inline-const-p, and in fact, I cannot find any code in the emacs lisp
> directory, or in the source of a couple of thousand packages, that
> makes use of inline-const-p or inline-const-val *other* than this
> exact function.

After some more investigation, the only other code I've seen that uses
define-inline to do more than defsubst would (as I understand it) is
in gnus-sum.el:
(define-inline gnus-summary-article-header (&optional number)
  "Return the header of article NUMBER."
  (inline-quote
   (gnus-data-header (gnus-data-find
      ,(or number
                           (inline-quote (gnus-summary-article-number)))))))
And I believe that occurence of "number" should be
"(inline-constant-val number)".
One example where there could be a use of define-inline's additional
functionality is in:
(define-inline cconv--var-classification (binder form)
  (inline-quote
   (cdr (assoc (cons ,binder ,form) cconv-var-classification))))

That could be changed to
(define-inline cconv--var-classification (binder form)
  (inline-quote
   (cdr (assoc ,(inline-quote ,(cons (inline-const-val binder)
(inline-const-val form))_ cconv-var-classification))))

It's a slight difference, but it is an example where computation could
be moved to compile-time.

From these example, it seems there are three capabilities afforded by
the underlying implementation technique of define-inline:

1) Computing subexpressions that are compile-time constants (that's
the cconv example)
2) Eliminating one or more parameters known at compile-time (that's
what cl-typep does)
3) A "constexpr" (in C++ parlance) constructor, "inline-quote"

Automating the first one involves identifying the maximal constant
expression containing each potentially constant parameter, which is
hard in general.  But if we restrict the language handled by the
inliner, it might be doable.  For example, if we could assume no
macros implicitly bind any identifiers already in use, and parameters
marked with &const as a guarantee  that the result of the function
does not vary based on state associated with them, maybe that would be
enough to determine non-trivial subexpressions pure subexpressions
above rather than forcing the user to identify them explicitly by
unquoting.  There's also a need to identify variables that may be
side-effected but which do not escape the inlined context, so the
inlined function is "const" with respect to them.  For example, in
(lambda (n) (let ((acc 1)) (while (> n 0) (setq acc (* n acc) n (1-
n))) acc)), the "while" form is "pure" because the value of the
function is constant with respect to it.  Alternatively, the while
form could be written in CPS style that eliminates the side-effects,
but the point of the restrictions is to avoid actually performing that
analysis.

For the second,  I'm thinking that what the programmer wants to
express is that if the "type" parameter is constant, then reducing all
forms with pure operators with respect to type is a "pure" macro in
the sense that it will always produce the same expression, and that
expression has no occurrences of "type".  For example,  (cl-typep x
'integer) => (integerp x).   ,This is "pure" as a function
transforming source code forms.  I'm thinking the user could mark the
"val" parameter with "&opaque" to indicate that the cl-typep inliner
should curry cl-typep to evaluate type at compile-type (when constant)
and produce an expression that has no references to "type" and does
not reference "val" while computing it.

The third item is interesting in the case of the cconv example, where
"cons" can be called at compile time if it occurs in a pure expression
that is evaluated at compile time, but otherwise should be deferred to
run-time, whether the arguments are constant or not.  This is possible
because most values in lisp can be stored in source expressions -
C++'s constexpr constructors explicitly require a return value of a
literal type.

Just for the terms of the debate, I think the exclusion of "assoc"
from being a "pure" function is incorrect, *if* we extend the notion
of constants to include pure functions (or restrict the notion of
constants to exclude non-pure functions).  Then assoc is pure because
when all three arguments are constant (i.e. the test function is
pure), then the value is constant.

Lynn



  parent reply	other threads:[~2023-05-18 14:56 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-07 14:32 inline function expansion Lynn Winebarger
2023-05-07 17:51 ` Basile Starynkevitch
2023-05-07 19:48 ` Philip Kaludercic
2023-05-07 20:16   ` Lynn Winebarger
2023-05-08  0:21     ` Emanuel Berg
2023-05-08 11:12       ` Lynn Winebarger
2023-05-08  2:03   ` Lynn Winebarger
2023-05-11  7:11   ` Lynn Winebarger
2023-05-12  6:25     ` Emanuel Berg
2023-05-18 14:56     ` Lynn Winebarger [this message]
2023-05-19 13:31       ` Stefan Monnier
2023-05-20 14:18         ` Lynn Winebarger
2023-05-20 15:32           ` Stefan Monnier
2023-05-21 12:47             ` Lynn Winebarger
2023-05-18 18:29     ` Stefan Monnier
2023-05-19  0:22       ` Lynn Winebarger
2023-05-19 13:07         ` Stefan Monnier
2023-05-20 15:01           ` Lynn Winebarger
2023-05-20 15:48             ` Stefan Monnier
2023-05-27 14:34               ` Lynn Winebarger
2023-05-28 14:12                 ` Lynn Winebarger
2023-05-28 14:57                 ` Stefan Monnier
2023-05-28 22:42                   ` Lynn Winebarger
2023-05-29  2:59                     ` Stefan Monnier
2023-06-06 22:38                       ` Lynn Winebarger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAM=F=bADJuA5TNgL_mxZrv7XMvcQUKVF1Y6AaABehpnz=9CmJQ@mail.gmail.com' \
    --to=owinebar@gmail.com \
    --cc=help-gnu-emacs@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    --cc=philipk@posteo.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.