help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: inline function expansion


From: Lynn Winebarger
Subject: Re: inline function expansion
Date: Sat, 27 May 2023 10:34:23 -0400

On Sat, May 20, 2023 at 11:48 AM Stefan Monnier
<monnier@iro.umontreal.ca> wrote:
> > For my purposes, an "interface" spec is just a list of method and slot
> > signatures associated to some object, which will be realized by a set
> > of bindings specified via special forms and bound to some name.
> > Then a compile-time generic f is a factoring of a regular generic into
> > a generic function of interface realizations that produces a concrete
> > runtime function.  I'm thinking to adopt the "place name" technique of
> > naming the concrete function by the form that created it, ie. \(f\
> > arg1-iface-realizer\ arg2-iface-realizer\ ...\).  Note the check is
> > that the realizer provides all the required signatures (a superset),
> > which is why I refer to it as a "constraint" on the interface
> > implementation.
> >
> > Then the generic method could dispatch on simple wrappers created by
> > the iface-realizers.  If constructors are invoked at compile time when
> > they appear in constant expressions and have constant arguments, then
> > the generic multi-dispatch method specialized on those realizers can
> > inline the call to the specialized method in the compiler macro, while
> > a residual generic method could do the same dispatch at run-time.
> >
> > But the point is to separate the expression of the algorithm from the
> > expression of the bindings that are used by the algorithm, without
> > resorting to either dynamic dispatch tables or ad hoc macros per
> > algorithm.
>
> I'm still lost.  An example might help.

For my purposes, interfaces and realizations of interfaces are just a
way to specify bindings of symbols in a more structured and
encapsulated way than raw macros.
I'm still spit-balling here, but I'm thinking a generic
compile-time-dispatched (inlineable) "max" might be defined along
these lines:

;; ordering interface has two parameters
(define-interface ordering (bool-T elt-T)
  ;; return value of `lt' method satisfies bool-T interface
  (method (bool-T lt) (elt-T a) (elt-T b)))

(define-realized-interface integer-ordering (ordering boolean integer)
  ;; bind lt method to built-in `<'
  (method lt <))

(defgeneric max (< a b)
  "Determine the larger of a and b according to <")

(define-inline-method max ((ordering <) &opaque a b)
  "Determine the larger of a and b according to ordering <"
  ;; < is treated syntactically as a dispatcher
  (if (< lt a b) a b))

;; because elisp does not allow applications in the operator position
(define-inlined-method integer-max (max integer-ordering))
;; Alternatively,
;; (integer-ordering lt)  reduces at compile time to something like
;;  #s(interface-method
;;     #s(interface-realization
;;        #s(interface ordering boolean integer)
;;        <)
;;      lt
;;      <)
;; which `max' is specialized for by define-inline-method above, so
(defun integer-max (a b)
  ;; inlined by compile-time dispatch of the max generic method
  (max (integer-ordering lt) a b))

;; These should produce t
(= (max integer-ordering 5 6) (integer-max 5 6))
(= (max (integer-ordering lt) 5 6) (integer-max 5 6))
(macroexp-const-p (macroexpand-all '(max (integer-ordering lt) 5 6)))

>
> >>     (define-inline my-plus (&rest args)
> >>       (inline-letevals args
> >>         (if (seq-every-p (lambda (x) (inline-const-p x)) args)
> >>             (apply #'+ (mapcar (lambda (x) (inline-const-val x)) args))
> >>           (inline-quote (+ . ,args)))))
> >>
> >> seems to do (more or less) what your code illustrated.
> >
> > But then inline-const-val will (and inline-const-p) will get the
> > symbol 'x as the operand, right?
>
> I don't think so.  Have you tried it (Edebug should work correctly)?

Ok, now I see why inline--testconst-p expands to include a call to
macroexp-const-p.

> >> I'm still not sure why you're not using a `compiler-macro` which seems
> >> to be exactly what you're after.
> >
> > I'm very finicky I suppose.  I want to get constant expression
> > evaluation as automatically as possible, to enable the compile-time
> > dispatch cleanly.  Or are you saying that generic methods can be
> > directly made into compiler macros?
>
> And here I'm lost as well.  AFAICT there's just as little "directly made
> into" for define-inline as for compiler-macros (`define-inline` is just
> a way to define a function and its `compiler-macro` in one go).

Hopefully the example above clarified what I'm talking about - `max'
is specialized at compile-time, and reduced completely at compile-time
as well if all the arguments are constant.  Although I would change
"inline-const-p" to test for function purity (exclude stateful
closures or otherwise impure functions).

> > I was thinking I needed the original symbol redefined to avoid
> > multiple macro expansions of the operands,
>
> `compiler-macro`s can decide to keep the call as-is without introducing
> an infinite macro-expansion loop.
>
I was concerned about an exponential rather than infinite number of
expansions , though I may have just been thinking conservatively.

Lynn



reply via email to

[Prev in Thread] Current Thread [Next in Thread]