From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Stefan Monnier Newsgroups: gmane.emacs.devel Subject: Re: Generators (iterators) for Gnu Emacs Date: Thu, 04 Dec 2014 20:55:21 -0500 Message-ID: References: <877fy77zhp.fsf@web.de> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: ger.gmane.org 1417744551 31755 80.91.229.3 (5 Dec 2014 01:55:51 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Fri, 5 Dec 2014 01:55:51 +0000 (UTC) Cc: Daniel Colascione , Emacs Development To: Michael Heerdegen Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Dec 05 02:55:44 2014 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1Xwi7S-0007OK-HD for ged-emacs-devel@m.gmane.org; Fri, 05 Dec 2014 02:55:42 +0100 Original-Received: from localhost ([::1]:48439 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Xwi7R-00034o-PD for ged-emacs-devel@m.gmane.org; Thu, 04 Dec 2014 20:55:41 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:57349) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Xwi7G-00034O-Vt for emacs-devel@gnu.org; Thu, 04 Dec 2014 20:55:38 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Xwi79-0002tO-A9 for emacs-devel@gnu.org; Thu, 04 Dec 2014 20:55:30 -0500 Original-Received: from ironport2-out.teksavvy.com ([206.248.154.181]:14272) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Xwi79-0002sd-5W for emacs-devel@gnu.org; Thu, 04 Dec 2014 20:55:23 -0500 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AjwPAOwQflRMCqTq/2dsb2JhbABSCYMHg2CFWsUdBAICgSQXAQEBAQEBfIQDAQEDAVYjBQsLNBIUGA0kiEoJ1lkBAQEBBgEBAQEeimCFNARXB4RIBYsBjFSFXYM4jDuCCoF4hBkhgTWBQgEBAQ X-IPAS-Result: AjwPAOwQflRMCqTq/2dsb2JhbABSCYMHg2CFWsUdBAICgSQXAQEBAQEBfIQDAQEDAVYjBQsLNBIUGA0kiEoJ1lkBAQEBBgEBAQEeimCFNARXB4RIBYsBjFSFXYM4jDuCCoF4hBkhgTWBQgEBAQ X-IronPort-AV: E=Sophos;i="5.07,502,1413259200"; d="scan'208";a="99599723" Original-Received: from 76-10-164-234.dsl.teksavvy.com (HELO pastel.home) ([76.10.164.234]) by ironport2-out.teksavvy.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 04 Dec 2014 20:55:21 -0500 Original-Received: by pastel.home (Postfix, from userid 20848) id 37F508848; Thu, 4 Dec 2014 20:55:21 -0500 (EST) In-Reply-To: <877fy77zhp.fsf@web.de> (Michael Heerdegen's message of "Fri, 05 Dec 2014 00:43:14 +0100") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.0.50 (gnu/linux) X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 206.248.154.181 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:178869 Archived-At: > I want to contribute a package about generators to Gnu Emacs. Thanks. Looks good. I generally prefer adding packages to GNU ELPA, but I guess it also makes sense to add this directly to Emacs. > Daniel Colascione suggested to me to rename the thing to "iterators.el", > since this package doesn't use continuation passing style to implement > "yield". I guess that's reasonable. I don't have any opinion on the naming. A few comments below. Stefan > ;;; Commentary: Please state clearly which are all the basic operations one can perform on a generator (AFAICT there's only one such operation, which is `gen-next'). It's great to see that you haven't needed anything else than `gen-next'. I'd have expected a `gen-done-p' test to be needed/handy every once in a while. > (defmacro gen-make (&rest body) > "Return a generator that evaluates BODY to generate elements. > For each call of `gen-next' with the the returned generator, BODY > will be evaluated to produce an element." > (let ((this-element (make-symbol "this-element"))) > `(let (,this-element) > (lambda () AFAICT this requires lexical-binding in the caller, so we might want to signal an error if lexical-binding is nil. > (if (eq ,this-element 'gen-done) > 'gen-done > (setq ,this-element (progn ,@body))))))) This code breaks down if your generator happens to return the symbol `gen-done'. A better option is to have a (defconst gen-done ) where the value is unique w.r.t `eq' (can be a string, a cons cell, an uninterned symbol, you name it) and then to use this value rather than hard-coding an interned symbol. > (defun gen-next (generator) > (funcall generator)) A defalias would be more efficient. > (if (not (null elements)) Aka (if elements > The sequence of returned elements is starting with VALUE. Any ^^^ Add extra space (or break the line). > successive element will be found by calling FUNCTION on the > preceding element." > (gen-append > (gen-from-elts value) > (gen-make (setq value (funcall function value))))) I guess a `gen-cons' would be more efficient than this (gen-append (gen-from-elts value) ...). > (defun gen-number-range (&optional start end inc) > "Return a generator of a number sequence. > START denotes the first number and defaults to 1. Tradition in computer science is to start counting from 0. There are some notable exceptions (such as `point-min', which I regret), but I strongly recommend to follow the tradition. > The second optional argument END specifies the upper limit. If nil, > the returned generator is infinite. INC is the increment used between > numbers and defaults to 1." Please be more precise about whether END is included or excluded. Tradition (see again) is to exclude the upper bound (e.g. in dotimes). > (defmacro gen-delay-expr (expression) Have you made use of this? I'm not sure it really fits. I mean, technically it works, but I'm not completely sure if pretending it's a generator is a clever idea or not. > (let ((done nil) el) > (gen-make > (if done 'gen-done > (setq el (gen-next generator)) > (while (and (not (eq el 'gen-done)) > (not (funcall predicate el))) > (setq el (gen-next generator))) > (if (eq el 'gen-done) > (setq done 'gen-done) > el))))) Better move the `el' into the `gen-make' so it's not caught as a free variable of the closure. And please apply de-Morgan's law. > (let ((current (pop generators)) el (done nil) (skip (make-symbol "skip"))) > (gen-delq > skip > (gen-make > (if done 'gen-done > (setq el (gen-next current)) > (if (eq el 'gen-done) > (if (null generators) > (progn (setq done t) 'gen-done) > (setq current (pop generators)) > skip) > el))))))) Same here: sink `el' into the `gen-make'. > (nreverse (gen-reduce (lambda (x y) (cons y x)) () generator))) Too bad the order of arguments was not swapped so we could use #'cons instead of this lambda. > "Return true if PREDICATE is true for any element of GENERATOR." ^^^^ We call it "non-nil". Same for `gen-every'. > (cl-callf or function #'identity) > (gen-max generator (and function (lambda (x) (- (funcall function x)))))) AFAICT, the first line makes sure `function' is not nil, so you can remove the "(and function" on the second line.