all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Faking local dynamic function bindings
@ 2014-01-29  2:04 Joost Kremers
  2014-01-29  3:50 ` Stefan Monnier
       [not found] ` <mailman.13080.1390967480.10748.help-gnu-emacs@gnu.org>
  0 siblings, 2 replies; 4+ messages in thread
From: Joost Kremers @ 2014-01-29  2:04 UTC (permalink / raw)
  To: help-gnu-emacs

Hi all,

I sorta trialed and errored my way to the following code (simplified[1]):

```
(defun foo (sexpr)
  (eval
   `(cl-macrolet ((contains (regexp)
                            `(string-match ,regexp string)))
      (sort (delq nil (mapcar #'(lambda (item)
                                  (let ((string (assq item strings-alist)))
                                    (when ,sexpr)
                                    string))
                              items-alist))
            'string<))))
```

Assume that `items-alist` is an alist with (SYMBOL . STRING) items.

`sexpr` is an interactively built expression that is used to filter
items-alist. It is a single s-expression and is runnable Elisp code,
provided that a macro `contains` is defined. Because I don't want to
define this macro globally (I don't want to have to put a "package"
prefix on it), I tried to define it locally.

That didn't work at first, because the expression `sexpr` is generated
at run-time, which means it cannot be in the lexical scope of
cl-macrolet. So I ended up creating a larger expression containing the
cl-macrolet and eval'ing that.

I have two questions:

- Is there a better ways of doing this? Of course, flet creates dynamic
  function bindings, but it's deprecated. The suggested alternative,
  `(cl-letf (symbol-value 'bar) ...)` doesn't work, because it requires
  that `bar' has a (global) symbol-value, which `contains` in this
  example does not.

- Can't this function be defined as a macro? It seems to me that a
  backquoted expression that is subsequently eval'ed is essentially the
  same as a macro, but my attempts to write foo as a macro have failed.

TIA

Joost




[1] The actual code is here:

https://github.com/joostkremers/ebib/blob/master/ebib.el#L3478



-- 
Joost Kremers                                   joostkremers@fastmail.fm
Selbst in die Unterwelt dringt durch Spalten Licht
EN:SiS(9)


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Faking local dynamic function bindings
  2014-01-29  2:04 Faking local dynamic function bindings Joost Kremers
@ 2014-01-29  3:50 ` Stefan Monnier
       [not found] ` <mailman.13080.1390967480.10748.help-gnu-emacs@gnu.org>
  1 sibling, 0 replies; 4+ messages in thread
From: Stefan Monnier @ 2014-01-29  3:50 UTC (permalink / raw)
  To: help-gnu-emacs

> (defun foo (sexpr)
>   (eval
>    `(cl-macrolet ((contains (regexp)
>                             `(string-match ,regexp string)))
>       (sort (delq nil (mapcar #'(lambda (item)
>                                   (let ((string (assq item strings-alist)))
>                                     (when ,sexpr)
>                                     string))
>                               items-alist))
>             'string<))))

IIUC, sexpr is only known very late (like much later than when you
byte-compile this file).  If that's the case, there's no avoiding `eval'.
But you can move the eval closer to sexpr:

(defun foo (sexpr)
  (sort (delq nil (mapcar #'(lambda (item)
                              (let ((string (assq item strings-alist)))
                                (when (eval
                                        `(cl-macrolet ((contains (regexp)
                                                        `(string-match ,regexp ,string)))
                                           ,sexpr))
                                  string))
                          items-alist))
        #'string<)))

And of course, you don't need `contains' to be a macro:
            
(defun foo (sexpr)
  (sort (delq nil (mapcar #'(lambda (item)
                              (let ((string (assq item strings-alist)))
                                (when (eval
                                        `(cl-flet ((contains (regexp)
                                                     (string-match regexp ,string)))
                                           ,sexpr))
                                  string))
                          items-alist))
        #'string<)))


-- Stefan




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Faking local dynamic function bindings
       [not found] ` <mailman.13080.1390967480.10748.help-gnu-emacs@gnu.org>
@ 2014-01-29  9:05   ` Joost Kremers
  2014-01-29 13:16     ` Stefan Monnier
  0 siblings, 1 reply; 4+ messages in thread
From: Joost Kremers @ 2014-01-29  9:05 UTC (permalink / raw)
  To: help-gnu-emacs

EN:SiS(9)
Stefan Monnier wrote:
>> (defun foo (sexpr)
>>   (eval
>>    `(cl-macrolet ((contains (regexp)
>>                             `(string-match ,regexp string)))
>>       (sort (delq nil (mapcar #'(lambda (item)
>>                                   (let ((string (assq item strings-alist)))
>>                                     (when ,sexpr)
>>                                     string))
>>                               items-alist))
>>             'string<))))
>
> IIUC, sexpr is only known very late (like much later than when you
> byte-compile this file).

Yes, you understand correctly.

>  If that's the case, there's no avoiding `eval'.

This is the point where I hit myself over the head and say duh... That
should have been obvious, even to me.

> But you can move the eval closer to sexpr:
>
> (defun foo (sexpr)
>   (sort (delq nil (mapcar #'(lambda (item)
>                               (let ((string (assq item strings-alist)))
>                                 (when (eval
>                                         `(cl-macrolet ((contains (regexp)
>                                                         `(string-match ,regexp ,string)))
>                                            ,sexpr))
>                                   string))
>                           items-alist))
>         #'string<)))

This resembles an earlier version of my code, but AFAIU it has the
disadvantage that the local macro binding is created and destroyed every
time the anonymous function is executed, which I guess creates extra
overhead and makes the function slower. That's why I moved the
cl-macrolet outside the mapcar. (Though I haven't done any profiling and
the older version was fast enough not to be annoying...)

> And of course, you don't need `contains' to be a macro:

True, of course. However, I'm using a macro because in the actual code
one of the arguments of `contains` is an unquoted symbol. (That's an
unfortunate design decision I made years ago that has proven to be
completely pointless and impractical. I've been meaning to change it,
but haven't found the time to do so yet.)

Thanks,


Joost

-- 
Joost Kremers                                   joostkremers@fastmail.fm
Selbst in die Unterwelt dringt durch Spalten Licht


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Faking local dynamic function bindings
  2014-01-29  9:05   ` Joost Kremers
@ 2014-01-29 13:16     ` Stefan Monnier
  0 siblings, 0 replies; 4+ messages in thread
From: Stefan Monnier @ 2014-01-29 13:16 UTC (permalink / raw)
  To: help-gnu-emacs

> This resembles an earlier version of my code, but AFAIU it has the
> disadvantage that the local macro binding is created and destroyed every
> time the anonymous function is executed, which I guess creates extra
> overhead and makes the function slower.

If that turns out to be a problem, you can do it as follows:

   (defun foo (sexpr)
     (let ((macroenv `((contains
                        . ,(lambda (regexp)
                             `(string-match ,regexp ,string))))))
       (sort
        (delq nil
              (mapcar
               (lambda (item)
                 (let ((string (assq item strings-alist)))
                   (when (eval (macroexpand-all sexpr macroenv))
                     string))
               items-alist)))
        #'string<)))


-- Stefan




^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-01-29 13:16 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-29  2:04 Faking local dynamic function bindings Joost Kremers
2014-01-29  3:50 ` Stefan Monnier
     [not found] ` <mailman.13080.1390967480.10748.help-gnu-emacs@gnu.org>
2014-01-29  9:05   ` Joost Kremers
2014-01-29 13:16     ` Stefan Monnier

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.