emacs-devel
[Top][All Lists]
Advanced

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

Re: Always-true predicate?


From: Pip Cet
Subject: Re: Always-true predicate?
Date: Fri, 19 Feb 2021 19:04:34 +0000

On Fri, Feb 19, 2021 at 3:07 PM Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> >> Maybe we should let `lambda` take arguments like Scheme does, i.e.
> >> (lambda (a b . c) FOO) instead of (lambda (a b &rest c) FOO), and in that
> >> case we could simple use "lambda _" as a shorthand for "constantly".
> > That would break things like pcase-lambda, though: we would no longer
> > be able to generalize lambda meaningfully.
>
> [ Not sure if you missed the implied smiley or if you're going along with
>   the crazy idea.  ]

I mistook it for a serious proposal, sorry.

> `pcase-lambda` wouldn't have to follow suit, so it'd be affected only if
> it decides to.

Then its docstring would need changing, because it would no longer be
like lambda.

> > Anyway, my problem with variadic functions isn't defining them, it's
> > calling them. I think I should be able to say
> >
> > (f a b c &rest d)
> >
> > rather than
> >
> > (apply #'f a b c d)
>
> I don't think it's sufficiently nicer to justify making such a change,

I think "usage mimics declaration" is a very important concept, but if
we're making sunk-cost arguments, one could argue against fairly much
any change...

> OTOH, the nice thing about `apply` is that it's just a function: no
> special treatment, no new syntax, nothing.  Try `grep apply
> lisp/emacs-lisp/bytecomp.el` and see how it really needs no
> special treatment.

...but it gets that special treatment, which is why (apply #'or) is
mis-compiled.

Note that it would be valid, but not required, for us to define what
(or &rest args) means, something apply cannot provide.

> > (f &rest a &rest b) = (apply #'f (append a b))
> >
> > and
> >
> > (f &rest keywords values) = (apply #'f (zip keywords values))
>
> I find the left hand side worse than the right hand side, FWIW.

That's probably a matter of taste and JavaScript exposure :-)

> I'd rather reduce the use of `&rest` and `apply` than try to make it
> more sexy, since it's fundamentally quite inefficient

I think the inefficiency is fundamental to "apply", not to "&rest".
When you call a known procedure all the packing and unpacking of
arguments can be inlined...

> (since it ends up
> having to take the list of args, put them on the stack only to recreate
> a new list for them on the other side; in theory it can optimized in
> many cases, but in practice it's extremely hard to find those cases
> reliably).

I agree our current way of doing things needs work, FWIW.

> > And if we can require optional arguments, why can't we provide them
> > optionally? For example, let's say in Emacs 33 we want to expand
> > copy-marker with a new argument to more clearly describe how the
> > marker repositions itself relative to other markers (or implicit)
> > markers at the same character position. But (copy-marker marker nil
> > &optional 'something) would work in Emacs 32 (which would include the
> > optionally-provided argument extension), and be equivalent to
> > (copy-marker marker nil) there.
>
> This seems difficult to make it work reliably.
> E.g. imagine the case where you've added an advice to `copy-marker`, so
> it's now redefined to
>
>     (lambda (rest args)
>       (message "About to call copy-marker")
>       (apply #<subr copy-marker> args))
>
> how would the caller know whether it should pass that `&optional 'something` ?

The alternative is to use func-arity at runtime, which breaks just as badly.

I think we're back to a fundamental question here: if I understand
correctly, to you, an ELisp function is fundamentally of the "take a
list, return a Lisp object or throw" type, as though it were defined
like this:

(defan f args (if (/= (length args) 2) (throw
'wrong-number-of-arguments)) (+ (car args) (cadr args)))

except not even the symbol "args" is part of the interface or function type.

To me, a function is defined like

(defun f (a b) "Add A and B." (+ a b))

The docstring, argument list, argument names, function name, and the
actual lambda are all part of what makes "f" "f", and it's legitimate
to use them all when calling it.

In your world, (lambda (&rest args) (apply #'f args)) is the same as
f. In my world, we have "func-arity", to which it isn't.

So, yes, wrappers would need a special symbol to use instead of &rest
which would make them receive (at run time) all the information the
caller provided (potentially at compile time). That would also be the
natural place to trap calls inquiring about the docstring, function
name, or arg spec.

So something like

(defun wrap (f)
  "Wrap F into a function object indistinguishable from it."
  (lambda (&universal-arg-list args)
    (cond ((eq args 'docstring)
       (function-docstring f))
      ((eq args 'argspec)
       (function-argspec f))
      ((eq args 'name)
       (function-name f))
      ((apply-universal-arg-list f args)))))

> I can see ways it could work, but I can't see an easy way to get there
> from where we are without introducing either a lot of extra complexity
> (and runtime cost) in the implementation, or backward incompatibility
> for some use cases, or both.

I don't think we can have all three, but then, we're in a pretty sorry
state to begin with. As far as I know, there's no way to define a
wrapper today that preserves arity, docstring, and arg spec, not even
if you cons and eval a lambda list at run time. On the other hand,
there's no way to define a "fast" wrapper function even if you keep
all those things fixed, because you have to go through the apply
machinery, which is fairly slow at present.

Pip



reply via email to

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