Hello Chickeneers,

find attached a suggested addition to miscmacros: the thrush operator
family. Apparently it stems from Raymond Smullyan's book on
combinatorial logic "To Mock a Mockingbird", which I haven't read
(yet). I've first learned about it through Clojure and have found it
quite nice for increasing the readability of chained calls. See Debasish
Ghosh's post [1] about it for some background.

As a practical example, consider this code:

  (write (fold + 0
               (map (lambda (i) (* 2 i)) 
                    (iota 10))))

Using the thrush operator it can be written like this:

  (->> (iota 10)
       (map (lambda (i) (* 2 i)))
       (fold + 0)
       (write))

which basically expands to the expression above, i.e. it inserts each
expression as the last argument of the following expression. I find it
much easier to read though since the order of evaluation is the order of
appearance in the code. I like to think of it like `let*' as opposed to
nested `lambda':

  ((lambda (x) ((lambda (y) ((lambda (z) (+ x y z)) 3)) 2)) 1)

versus:
  
  (let* ((x 1) (y 2) (z 3)) (+ x y z))

Much easier to track what's going on there as stuff that belongs
together is visually close to each other. 

There's also `->' which inserts each expression as the first argument of
the following one. The difference:

  (->> 10 (- 3)) ; => -7
  (-> 10 (- 3))  ; => 7

For kicks and fun I also added starred versions (`->*' and `->>*') which
are multi-value aware. Not sure how useful that is and I didn't want to
add it to the default macros to avoid the overhead. But it's good to
have them just for completeness sake!

I guess that's about it. I'm not sure if adding it to miscmacros is a
great idea, might as well just create a `thrush' egg. OTOH we have the
similar `doto' macro in there already. Comments?


Moritz

[1] http://debasishg.blogspot.com/2010/04/thrush-in-clojure.html
Index: miscmacros.scm
===================================================================
--- miscmacros.scm	(revision 25904)
+++ miscmacros.scm	(working copy)
@@ -9,13 +9,14 @@
    define-optionals define-parameter define-enum
    ignore-values ignore-errors
    ecase
-   define-syntax-rule)
+   define-syntax-rule
+   -> ->* ->> ->>*)
 
   (import scheme)
   ;; No effect -- caller must import these manually.
   (import (only chicken
                 when unless handle-exceptions let-optionals make-parameter
-                add1 sub1))
+                add1 sub1 define-for-syntax))
 
 ;;; Modify locations, T-like:
 
@@ -303,4 +304,55 @@
     clauses ...
     (else (error "no valid case" val))))
 
+(define-for-syntax (expand-thrush x weave)
+  (let loop ((y (cdr x)) (form (car x)))
+    (if (null? y)
+        form
+        (let ((z (car y)))
+          (loop (cdr y)
+                (weave z form))))))
+
+(define-syntax ->
+  (ir-macro-transformer
+   (lambda (x i c)
+     (expand-thrush
+      (cdr x)
+      (lambda (z form)
+        (cons (car z)
+              (cons form (cdr z))))))))
+
+(define-syntax ->*
+  (ir-macro-transformer
+   (lambda (x i c)
+     (expand-thrush
+      (cdr x)
+      (lambda (z form)
+        `(receive args ,form
+           (apply
+            ,(car z)
+            (append args (list . ,(cdr z))))))))))
+
+(define-syntax ->>
+  (ir-macro-transformer 
+   (lambda (x i c)
+     (expand-thrush
+      (cdr x)
+      (lambda (z form)
+        (append z (list form)))))))
+
+(define-syntax ->>*
+  (ir-macro-transformer 
+   (lambda (x i c)
+     (expand-thrush
+      (cdr x)
+      (lambda (z form)
+        `(receive args ,form
+           (apply
+            ,(car z)
+            (append (list . ,(cdr z)) args))))))))
+
 )
+
+
+
+
Index: tests/run.scm
===================================================================
--- tests/run.scm	(revision 0)
+++ tests/run.scm	(working copy)
@@ -0,0 +1,22 @@
+(use test srfi-1 miscmacros)
+
+(test 1 (-> 99 (/ 11) (/ 9)))
+
+(test '(1 2 3 4)
+      (->* (values 1 2)
+           (list 3)
+           (append '(4))))
+
+(test 7 (-> 10 (- 3)))
+(test -7 (->> 10 (- 3)))
+
+(test 9 (->> 1 (+ 2) (* 3)))
+
+(test 9 (->> '(1 2 3)
+             (map add1)
+             (fold + 0)))
+
+(test '((foo . 100) (bar . 200))
+      (->>* (values '(foo bar) '(100 200))
+            (map cons)))
+
_______________________________________________
Chicken-hackers mailing list
Chicken-hackers@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-hackers

Reply via email to