all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Tomas Hlavaty <tom@logand.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Jim Porter <jporterbugs@gmail.com>,
	Karthik Chikmagalur <karthikchikmagalur@gmail.com>,
	Thomas Koch <thomas@koch.ro>,
	"emacs-devel@gnu.org" <emacs-devel@gnu.org>
Subject: Re: continuation passing in Emacs vs. JUST-THIS-ONE
Date: Sun, 26 Mar 2023 21:35:27 +0200	[thread overview]
Message-ID: <87tty78fwg.fsf@logand.com> (raw)
In-Reply-To: <87a6001xm0.fsf@logand.com>

On Sat 25 Mar 2023 at 19:42, Tomas Hlavaty <tom@logand.com> wrote:
>> I don't think so, no.  But you would need fancy rewriting if you wanted
>> to allow
>>
>>     (concat foo (futur-let* (...) ...))

or one could do it explicitly:

   (concat foo (future-wait (futur-let* (...) ...)))

> Why do you recommend to poll with futur.el?

I see now that it is future-wait which requires it.

I think I managed to derive nicer async and await than futur.el:

Here a future is just a thunk which returns the resolved value, EAGAIN
if unresolved or throws an error.

(defun await (future)
  (let (z)
    (while (eq 'EAGAIN (setq z (funcall future)))
      ;; TODO poke sit-for/io on yield?  how?
      (sit-for 0.2))
    z))

(defmacro async (&rest body)
  (declare (indent 0))
  (let ((z (gensym))
        (e (gensym)))
    `(let (,e (,z 'EAGAIN))
       (cl-flet ((yield (x) (setq ,z x))
                 ;; TODO catch and re-throw instead of fail? how?
                 (fail (string &rest args) (setq ,e (cons string args))))
         ;; TODO add abort? how? is it a good idea?
         ,@body)
       (lambda () (if ,e (apply #'error ,e) ,z)))))

(await (async (yield (+ 1 41))))
(await (async (fail "hi %d" 42)))

That's it.

Now it would be good to run something in background.  I got the
following examples to work:

Assuming alet (async let) macro, which binds var when async process
finishes with the value of its output:

(alet p1 '("which" "emacs")
  (when p1
    (alet p2 `("readlink" "-f" ,p1)
      (when p2
        (message "@@@ %s" p2)))))

I can await async process:

(await
 (async
   (alet p1 '("which" "emacs")
     (when p1
       (alet p2 `("readlink" "-f" ,p1)
         (when p2
           (yield p2)))))))

or even await async process inside async process:

(await
 (async
   (alet p `("readlink" "-f" ,(await
                               (async
                                 (alet p '("which" "emacs")
                                   (when p
                                     (yield p))))))
     (when p
       (yield p)))))

This shows off async & await working with async process.  await is
annoying and not needed in this example as shown above but in some cases
it is necessary.

How does alet look like?

In the previous examples I processed the output of an async process per
line but futur.el example takes the whole output.  The only thing I need
to change is to change output chunking from line-writer to
buffer-writer and add a few convenience functions and macros:

(defmacro consume (var val &rest body)
  ;; set up async process and return immediately
  ;; body called repeatedly in background per process output event
  (declare (indent 2))
  `(funcall ,val (lambda (,var) ,@body)))

;; wrap in nicer syntax, async let, in background
(defmacro alet (var command &rest body)
  (declare (indent 2))
  (let ((cmd (gensym)))
    `(let ((,cmd ,command))
       (consume ,var (let ((b (test-buffer (format "*alet%s" ,cmd))))
                       (writer-process6
                        b
                        ,cmd
                        (lambda (writer) (buffer-writer b writer))))
         ,@body))))

;; customizeable chunking
(defun writer-process6 (buffer command chunk)
  (lambda (writer)
    (let ((w (funcall chunk writer)))
      (make-process :name (buffer-name buffer)
                    :command command
                    :buffer buffer
                    :sentinel (writer-sentinel w)
                    :filter (writer-filter w)))))

;; taken from (info "Process Filter Functions")
;; quite useful, why is this not part of emacs code?
(defun ordinary-insertion-filter (proc string)
  (when (buffer-live-p (process-buffer proc))
    (with-current-buffer (process-buffer proc)
      (let ((moving (= (point) (process-mark proc))))
        (save-excursion
          ;; Insert the text, advancing the process marker.
          (goto-char (process-mark proc))
          (insert string)
          (set-marker (process-mark proc) (point)))
        (if moving (goto-char (process-mark proc)))))))

;; like line-writer but output the whole thing
(defun buffer-writer (buffer writer)
  (let (done)
    (lambda (string)
      (unless done
        (if string
            (ordinary-insertion-filter (get-buffer-process buffer) string)
          (let ((z (with-current-buffer buffer (buffer-string))))
            (kill-buffer buffer)
            (funcall writer z))
          (funcall writer nil)
          (setq done t))))))

I think this provides nicer interface for async code than futur.el and
even comes with a working example.

Is there anything else async & await should handle but does not?



  reply	other threads:[~2023-03-26 19:35 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-11 12:53 continuation passing in Emacs vs. JUST-THIS-ONE Thomas Koch
2023-03-12  1:45 ` Jim Porter
2023-03-12  6:33   ` tomas
2023-03-14  6:39   ` Karthik Chikmagalur
2023-03-14 18:58     ` Jim Porter
2023-03-15 17:48       ` Stefan Monnier
2023-03-17  0:17         ` Tomas Hlavaty
2023-03-17  3:08           ` Stefan Monnier
2023-03-17  5:37             ` Jim Porter
2023-03-25 18:42             ` Tomas Hlavaty
2023-03-26 19:35               ` Tomas Hlavaty [this message]
2023-03-28  7:23                 ` Tomas Hlavaty
2023-03-29 19:00                 ` Stefan Monnier
2023-04-03  0:39                   ` Tomas Hlavaty
2023-04-03  1:44                     ` Emanuel Berg
2023-04-03  2:09                     ` Stefan Monnier
2023-04-03  4:03                       ` Po Lu
2023-04-03  4:51                         ` Jim Porter
2023-04-10 21:47                       ` Tomas Hlavaty
2023-04-11  2:53                         ` Stefan Monnier
2023-04-11 19:59                           ` Tomas Hlavaty
2023-04-11 20:22                             ` Stefan Monnier
2023-04-11 23:07                               ` Tomas Hlavaty
2023-04-12  6:13                                 ` Eli Zaretskii
2023-04-17 20:51                                   ` Tomas Hlavaty
2023-04-18  2:25                                     ` Eli Zaretskii
2023-04-18  5:01                                       ` Tomas Hlavaty
2023-04-18 10:35                                       ` Konstantin Kharlamov
2023-04-18 15:31                                         ` [External] : " Drew Adams
2023-03-29 18:47               ` Stefan Monnier
2023-04-17  3:46                 ` Lynn Winebarger
2023-04-17 19:50                   ` Stefan Monnier
2023-04-18  2:56                     ` Lynn Winebarger
2023-04-18  3:48                       ` Stefan Monnier
2023-04-22  2:48                         ` Lynn Winebarger
2023-04-18  6:19                     ` Jim Porter
2023-04-18  9:52                       ` Po Lu
2023-04-18 12:38                         ` Lynn Winebarger
2023-04-18 13:14                         ` Stefan Monnier
2023-04-19  0:28                           ` Basil L. Contovounesios
2023-04-19  2:59                             ` Stefan Monnier
2023-04-19 13:25                               ` [External] : " Drew Adams
2023-04-19 13:34                                 ` Robert Pluim
2023-04-19 14:19                                   ` Stefan Monnier
2023-04-21  1:33                                     ` Richard Stallman
2023-04-19  1:11                           ` Po Lu
2023-04-17 21:00                   ` Tomas Hlavaty
2023-03-14  3:58 ` Richard Stallman
2023-03-14  6:28   ` Jim Porter
2023-03-16 21:35 ` miha
2023-03-16 22:14   ` Jim Porter
2023-03-25 21:05 ` Tomas Hlavaty
2023-03-26 23:50 ` Tomas Hlavaty

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

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

  git send-email \
    --in-reply-to=87tty78fwg.fsf@logand.com \
    --to=tom@logand.com \
    --cc=emacs-devel@gnu.org \
    --cc=jporterbugs@gmail.com \
    --cc=karthikchikmagalur@gmail.com \
    --cc=monnier@iro.umontreal.ca \
    --cc=thomas@koch.ro \
    /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 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.