emacs-devel
[Top][All Lists]
Advanced

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

Re: thunk.el: Document that thunk-force == funcall?


From: Tomas Hlavaty
Subject: Re: thunk.el: Document that thunk-force == funcall?
Date: Tue, 17 Nov 2020 23:42:39 +0100

On Tue 17 Nov 2020 at 22:07, Michael Heerdegen <michael_heerdegen@web.de> wrote:
> The result might not be defined at think time.  It might depend on "the
> environment".
>
> Say the code loops over a list of files or so (not known at think time).
> The thunk could "contain" a test that might take long (e.g. something
> that might need to look at the file's contents) but the result is
> interesting only sometimes, depending on the result of other tests (also
> not known at think time).

do you have a specific example i could learn from?

i use thunks a lot for a kind of minimalist portable streams (sometimes
called generators in lisp), e.g. see
https://logand.com/sw/emacs-pdf/file/emacs-pdf.el.html#l198

i am hoping to learn new use-case or trick here

your use-case seems to be like this:

(dolist (f (directory-files "/tmp/" t))
  (when (and (some-other-test-p)
             (something-is-the-case-but-it-takes-long-to-find-out-p))
    (do-something)))

why does something-is-the-case-but-it-takes-long-to-find-out-p need to
be a thunk?  the special form `and' is already lazy and skips computing
something-is-the-case-but-it-takes-long-to-find-out-p when
some-other-test-p is false.

also it is usually better to not depend on implicit environment but to
use function arguments when possible.  in this case
something-is-the-case-but-it-takes-long-to-find-out-p should take the f
as arg (it might need to look at the file's contents after all):

(dolist (f (directory-files "/tmp/" t))
  (when (and (some-other-test-p f)
             (something-is-the-case-but-it-takes-long-to-find-out-p f))
    (do-something f)))

one use-case for thunks here would be to make dolist lazy:

(setq lexical-binding t)
(require 'cl)

(defun brook (x)
  (etypecase x
    (list
      (lambda ()
        (pop x)))))

(defun brook-count (brook)
  (loop
   with z = nil
   while (setq z (funcall brook))
   count z))

(defun filter-brook (fn brook)
  (lambda ()
    (block yield
      (let (z)
        (while (setq z (funcall brook))
          (when (funcall fn z)
            (return-from yield z)))))))

;; now we can for example count regular files as we pull them lazily
(let ((some-other-test-p 'file-regular-p))
  (brook-count
   (filter-brook some-other-test-p
                 (brook (directory-files "/tmp/" t)))))

(defun map-brook (fn brook)
  (lambda ()
    (let ((x (funcall brook)))
      (when x
        (funcall fn x)))))

(defun brook-collect (brook)
  (loop
   with z = nil
   while (setq z (funcall brook))
   collect z))

(defun sha256sum (file)
  (with-temp-buffer
    (call-process "sha256sum" file (current-buffer))
    (buffer-substring-no-properties (point-min) (+ 64 (point-min)))))

;; now we can for example collect sha256sum of each regular file
(let ((some-other-test-p 'file-regular-p)
      (something-is-the-case-but-it-takes-long-to-find-out-p 'sha256sum))
  (brook-collect
   (map-brook
    something-is-the-case-but-it-takes-long-to-find-out-p
    (filter-brook some-other-test-p
                  (brook (directory-files "/tmp/" t))))))

;; or display progress during slow computation
(defun progress-brook (brook)
  (lambda ()
    (let ((z (funcall brook)))
      (when z
        (message (format "progress-brook: %s" z))
        z))))

(let ((some-other-test-p 'file-regular-p)
      (something-is-the-case-but-it-takes-long-to-find-out-p 'sha256sum))
  (brook-collect
   (progress-brook
    (map-brook
     something-is-the-case-but-it-takes-long-to-find-out-p
     (progress-brook
      (filter-brook some-other-test-p
                    (brook (directory-files "/tmp/" t))))))))

i can imagine that we might for example want to show progress also
during computation of sha256sum.
something-is-the-case-but-it-takes-long-to-find-out-p would then return
thunk and be run "step by step" like this:

(defun flat-brook (&rest brooks)
  (lambda ()
    (block yield
      (while brooks
        (let ((z (funcall (car brooks))))
          (cond
           ((functionp z) (push z brooks))
           (z (return-from yield z))
           (t (pop brooks))))))))

(let ((some-other-test-p 'file-regular-p)
      (something-is-the-case-but-it-takes-long-to-find-out-p
       (lambda (f)
         ;; i cannot easily show progress during sha256sum here so
         ;; lets immitate that by showing progress before and after
         (let ((pending
                (list
                 (lambda () (message "before sha256sum %s" f))
                 (lambda ()
                   (let ((z (sha256sum f)))
                     (message "sha256sum %s %s" f z)
                     z))
                 (lambda () (message "after sha256sum %s" f)))))
           (lambda ()
             (when pending
               (funcall (pop pending))))))))
  (brook-collect
   (flat-brook
    (map-brook
     something-is-the-case-but-it-takes-long-to-find-out-p
     (filter-brook some-other-test-p
                   (brook (directory-files "/tmp/" t)))))))

but in which use-case should
something-is-the-case-but-it-takes-long-to-find-out-p be a thunk?



reply via email to

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