unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Stefan Monnier <monnier@IRO.UMontreal.CA>
To: emacs-devel@gnu.org
Cc: Daniel Colascione <dancol@dancol.org>
Subject: Re: [Emacs-diffs] master f6b5db6: Add support for generators
Date: Tue, 03 Mar 2015 13:05:23 -0500	[thread overview]
Message-ID: <jwvzj7u80xx.fsf-monnier+emacsdiffs@gnu.org> (raw)
In-Reply-To: <E1YSZzA-0005BJ-Li@vcs.savannah.gnu.org> (Daniel Colascione's message of "Mon, 02 Mar 2015 23:42:52 +0000")

>     Add support for generators
>    
>     diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog
[...]
>     +	* automated/generator-tests.el: New tests

The content of the commit log is fine, but the form sucks.  Please get
rid of the diff-cruft in those commit messages.

> +  A @dfn{generator} is a function that produces a potentially-infinite
                            ^^^^^^^^
Do we want to document it as a function?  Maybe it would be better to
document it as an "object" of unspecified implementation (i.e. calling
it via funcall rather than via iter-next is unsupported).

> +@defmac iter-yield-from iterator
> +@code{iter-yield-from} yields all the values that @var{iterator}
> +produces and evaluates to the value that @var{iterator}'s generator
> +function returns normally.  While it has control, @var{iterator}
> +receives sent to the iterator using @code{iter-next}.
   ^^^^^^^^^^^^^
Some word(s) is missing here.

> +  To use a generator function, first call it normally, producing a
> +@dfn{iterator} object.

Ah, right, so that's why you defined generators as functions.  OK, now
that makes sense.  Maybe this description of the difference between
a generator and an iterator should be placed earlier, tho.

> +@defun iter-close iterator
> +If @var{iterator} is suspended inside a @code{unwind-protect} and
> +becomes unreachable, Emacs will eventually run unwind handlers after a
> +garbage collection pass.  To ensure that these handlers are run before
> +then, use @code{iter-close}.
> +@end defun

Hmm... but earlier you said:

   +@code{iter-yield} and @code{iter-yield-from} cannot appear inside
   +@code{unwind-protect} forms.

Oh, wait, I think I understand: we can use iter-yield inside the main
body of unwind-protect but not inside the unwind forms, right?  I think
this should be clarified.

To get back to iter-close, this seems to be a tricky aspect of the
implementation.  Could you give me more details, such as an example
problematic case where calling iter-close makes a difference?
I guess this has to do with those finalizers, so if you could explain
how/why these are used, I'd really appreciate it.

> +@example
> +(iter-defun my-iter (x)
> +  (iter-yield (1+ (iter-yield (1+ x))))
> +  -1 ;; Return normally
> +  )

Please don't leave a close-paren alone on its line in such example code.

> -	* vc/vc.el (vc-responsible-backend): Add autoload cooking for
> +	* vc/vc.el (vc-responsible-backend): Add autoload cookie for

Thanks ;-)

> +(defvar *cps-bindings* nil)
> +(defvar *cps-states* nil)
> +(defvar *cps-value-symbol* nil)
> +(defvar *cps-state-symbol* nil)
> +(defvar *cps-cleanup-table-symbol* nil)
> +(defvar *cps-cleanup-function* nil)
> +
> +(defvar *cps-dynamic-wrappers* '(identity)
> +  "List of transformer functions to apply to atomic forms we
> +evaluate in CPS context.")

This CL naming style is not used in Elisp.  I.e. just drop the
surrounding stars.

> +  (let* ((state (cl-gensym (format "cps-state-%s-" kind))))
                    ^^^^^^^^^
Elisp prefers make-symbol.

> +(defvar cps-disable-atomic-optimization nil

We usually use "inhibit" rather than "disable".

> +    ;; Unfortunately, because elisp lacks a mechanism for generically
> +    ;; capturing the reason for an arbitrary non-local control
> +    ;; transfer and restarting the transfer at a later point, we
> +    ;; cannot reify non-local transfers and cannot allow
> +    ;; continuation-passing code inside UNWINDFORMS.

I think it's an acceptable limitation.  Unwind-forms should be "short
and to the point".  We actually should run them with inhibit-quit (tho
we currently don't, which leads to some problems when the user hits C-g
repeatedly in panic).

> +(put 'iter-end-of-sequence 'error-conditions '(iter-end-of-sequence))
> +(put 'iter-end-of-sequence 'error-message "iteration terminated")

This should use the newish `define-error'.

> +(defmacro iter-yield-from (value)
> +  "When used inside a generator function, delegate to a sub-iterator.
> +The values that the sub-iterator yields are passed directly to
> +the caller, and values supplied to `iter-next' are sent to the
> +sub-iterator.  `iter-yield-from' evaluates to the value that the
> +sub-iterator function returns via `iter-end-of-sequence'."
> +  (let ((errsym (cl-gensym "yield-from-result"))
> +        (valsym (cl-gensym "yield-from-value")))
> +    `(let ((,valsym ,value))
> +       (unwind-protect
> +            (condition-case ,errsym
> +                (let ((vs nil))
> +                  (while t
> +                    (setf vs (iter-yield (iter-next ,valsym vs)))))
> +              (iter-end-of-sequence (cdr ,errsym)))
> +         (iter-close ,valsym)))))

[Mostly out of curiosity:] Could this be implemented as

   `(iter--blabla ,value (lambda (x) (iter-yield x)))

> +(defmacro iter-defun (name arglist &rest body)
> +  "Creates a generator NAME.
> +When called as a function, NAME returns an iterator value that
> +encapsulates the state of a computation that produces a sequence
> +of values.  Callers can retrieve each value using `iter-next'."
> +  (declare (indent defun))
> +  (cl-assert lexical-binding)
> +  `(defun ,name ,arglist
> +     ,(cps-generate-evaluator
> +       `(cl-macrolet ((iter-yield (value) `(cps-internal-yield ,value)))
> +          ,@body))))

IIUC you don't support any declarations between ARGLIST and BODY.
You could use macroexp-parse-body for that.

In cps-generate-evaluator I see you do:

> +                         (macroexpand-all form)

IIUC this will lose any local macro stored in
macroexpand-all-environment.  IOW you need to pass
macroexpand-all-environment to this macroexpand-all call.

Also, both calls to cps-generate-evaluator look identical, so I think
it'd be better to just pass `body' to cps-generate-evaluator, and then
have cps-generate-evaluator handle the local macro definition of iter-yield.

> +(eval-after-load 'elisp-mode
> +  (lambda ()
> +    (font-lock-add-keywords
> +     'emacs-lisp-mode
> +     '(("(\\(iter-defun\\)\\_>\\s *\\(\\(?:\\sw\\|\\s_\\)+\\)?"
> +        (1 font-lock-keyword-face nil t)
> +        (2 font-lock-function-name-face nil t))
> +       ("(\\(iter-next\\)\\_>"
> +        (1 font-lock-keyword-face nil t))
> +       ("(\\(iter-lambda\\)\\_>"
> +        (1 font-lock-keyword-face nil t))
> +       ("(\\(iter-yield\\)\\_>"
> +        (1 font-lock-keyword-face nil t))
> +       ("(\\(iter-yield-from\\)\\_>"
> +        (1 font-lock-keyword-face nil t))))))

Using fewer entries (at most 2) would make this more efficient
(especially since they all start with "(iter-)").


        Stefan



       reply	other threads:[~2015-03-03 18:05 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20150302234252.19883.48595@vcs.savannah.gnu.org>
     [not found] ` <E1YSZzA-0005BJ-Li@vcs.savannah.gnu.org>
2015-03-03 18:05   ` Stefan Monnier [this message]
2015-03-03 18:20     ` [Emacs-diffs] master f6b5db6: Add support for generators Daniel Colascione
2015-03-03 19:35       ` Stefan Monnier
2015-03-03 21:03         ` Daniel Colascione
2015-03-03 21:06           ` Daniel Colascione
2015-03-04  4:52             ` Stefan Monnier
2015-03-04  5:34               ` Daniel Colascione
2015-03-04 23:02                 ` Stefan Monnier
2015-03-04 23:05                   ` Daniel Colascione
2015-03-05  1:06                     ` Stefan Monnier
2015-03-04  4:51           ` Stefan Monnier
2015-03-03 19:51     ` Artur Malabarba
2015-03-05  0:01   ` Michael Heerdegen
2015-03-21  5:52     ` Michael Heerdegen
2015-03-21 13:19       ` Stefan Monnier
2015-03-25 13:09         ` Michael Heerdegen

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

  List information: https://www.gnu.org/software/emacs/

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

  git send-email \
    --in-reply-to=jwvzj7u80xx.fsf-monnier+emacsdiffs@gnu.org \
    --to=monnier@iro.umontreal.ca \
    --cc=dancol@dancol.org \
    --cc=emacs-devel@gnu.org \
    /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 public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).