dash.el icon indicating copy to clipboard operation
dash.el copied to clipboard

Sequential threading/anaphoric cond-like macro idea

Open alphapapa opened this issue 7 years ago • 7 comments

Note: It's no longer proposed that these macros be named -cond->> and --cond->. See this comment.

Hey, it's me again, another idea. :)

(defmacro -cond->> (test &rest forms)
  (declare (indent defun))
  `(cond ,@(cl-loop for form in forms
                    for this-test = (pcase (car form)
                                      ('t t)
                                      (_ (append test (list (car form)))))
                    collect (list this-test
                                  (cadr form)))))

Used like:

(-cond->> (<= difference)
  (14400 ;; Within past 4 hours
   100)
  (86400 ;; Within last day
   80)
  (259200 ;; Within last 3 days
   60)
  (604800 ;; Within last week
   40)
  (2419200 ;; Within last 4 weeks
   20)
  (7776000 ;; Within last 90 days
   10)
  (t ;; More than 90 days
   0))

Which expands to:

(cond ((<= difference 14400)
       100)
      ((<= difference 86400)
       80)
      ((<= difference 259200)
       60)
      ((<= difference 604800)
       40)
      ((<= difference 2419200)
       20)
      ((<= difference 7776000)
       10)
      (t 0))

Saves a bit of repetition. Could also have a -cond-> form that uses each cond test as the second argument in the test form rather than the last, like the difference between ->> and ->.

alphapapa avatar Sep 06 '17 04:09 alphapapa

Here's another example where this would be useful: https://github.com/zk-phi/sky-color-clock/blob/master/sky-color-clock.el#L263:

(defun sky-color-clock--emoji-moonphase (time)
  (let* ((time-in-days (/ (float-time time) 60 60 24))
         (phase (mod (- time-in-days sky-color-clock--newmoon) sky-color-clock--moonphase-cycle)))
    (cond ((<= phase  1.84) "🌑")
          ((<= phase  5.53) "🌒")
          ((<= phase  9.22) "🌓")
          ((<= phase 12.91) "🌔")
          ((<= phase 16.61) "🌕")
          ((<= phase 20.30) "🌖")
          ((<= phase 23.99) "🌗")
          ((<= phase 27.68) "🌘")
          (t                "🌑"))))

Which could be:

(defun sky-color-clock--emoji-moonphase (time)
  (let* ((time-in-days (/ (float-time time) 60 60 24))
         (phase (mod (- time-in-days sky-color-clock--newmoon) sky-color-clock--moonphase-cycle)))
    (-cond->> (<= phase)
      (1.84 "🌑")
      (5.53 "🌒")
      (9.22 "🌓")
      (12.91 "🌔")
      (16.61 "🌕")
      (20.30 "🌖")
      (23.99 "🌗")
      (27.68 "🌘")
      (t "🌑"))))

alphapapa avatar Jan 20 '18 21:01 alphapapa

By the way, an anaphoric version:

(defmacro --cond-> (test &rest forms)
  (declare (indent defun))
  `(cond ,@(cl-loop for (it result) in forms
                    when (eq it t)
                    do (setq test t)
                    collect (list `(let ((it ,it))
                                     ,test)
                                  result))))

e.g. FizzBuzz:

(cl-loop for n from 1 to 100
         collect (--cond-> (= 0 (% n it))
                   (15 (list n "FizzBuzz"))
                   (3 (list n "Fizz"))
                   (5 (list n "Buzz"))
                   (t n)))
;; => (1 2 (3 "Fizz") 4 (5 "Buzz") (6 "Fizz") 7 8 (9 "Fizz") (10 "Buzz") 11 (12 "Fizz") 13 14 (15 "FizzBuzz") ...)

alphapapa avatar May 24 '18 08:05 alphapapa

@alphapapa That's not how Clojure's cond-> and cond->> work, and doing something else will probably be confusing for most people. In Clojure, cond->> takes something and test form pairs, like (cond->> something test1 form1 test2 form2 ...) and it puts something last in form1 if test1 is true, then that in form2 if test2 is true, etc.

What you have described is more akind to Clojure's condp.

Clojure's cond->> https://clojuredocs.org/clojure.core/cond-%3E%3E Clojure's condp https://clojuredocs.org/clojure.core/condp

oskarkv avatar Mar 25 '20 10:03 oskarkv

Does #349 satisfy this feature request?

basil-conto avatar Dec 17 '20 16:12 basil-conto

@basil-conto Not exactly; the examples I showed don't work on that PR's code. However, @oskarkv's comment may explain why. Maybe the macro I've shown here should be named differently so -cond->> could behave as Clojure's does.

Of course, naming things is hard, but I'll change the title of this issue to...something else...

alphapapa avatar Dec 18 '20 04:12 alphapapa

Of course, naming things is hard, but I'll change the title of this issue to...something else...

Imagine the difficulty for someone reading Elisp code with all these many similar but subtly different "magic" macros. ;)

basil-conto avatar Dec 18 '20 11:12 basil-conto

Imagine the difficulty for someone reading Elisp code with all these many similar but subtly different "magic" macros. ;)

Yes, that's probably why not many of them have been implemented in a package. ;)

alphapapa avatar Dec 19 '20 06:12 alphapapa