News for hackers. Stuff that matters.
Re:Programming paradigms that change how people think about ... by kazinator on Thursday March 07, 2019 @08:46PM (#19334450)
Out of order calculation based on dependencies: mlet (magic let/mutual let) macro:

  This is the TXR Lisp interactive listener of TXR 212.
  Quit with :quit or Ctrl-D on empty line. Ctrl-X ? for cheatsheet.
  1> (mlet ((w 3)
            (x (+ z w))
            (z (* 2 w)))
       (list x z w))
  (9 6 3)
Obligatory macro-expansion:

  2> (macroexpand '(mlet ((w 3)
            (x (+ z w))
            (z (* 2 w)))
       (list x z w)))
  (let (#:g0011 #:g0012
        #:g0013)
    (symacrolet ((w (force #:g0011))
                 (x (force #:g0012))
                 (z (force #:g0013)))
      (set #:g0011
        (delay 3))
      (set #:g0012
        (delay (+ z w)))
      (set #:g0013
        (delay (* 2 w)))
      (list x z w)))
Circular dep naturally fails:

  1> (mlet ((x y) (y z) (z x)))
  nil
  2> (mlet ((x y) (y z) (z x)) x)
  ** (expr-2:1) force: recursion forcing delayed form y (source location n/a)
Re:Programming paradigms that change how people think about ... by GorgeRonde on Friday March 08, 2019 @02:22PM (#19341175)
I wrote the exact same macro in Clojure a couple weeks ago !

  (defmacro lay [[sym expr & more-bindings] & body]
    (let [delay-sym (gensym (str "laid-" sym "-"))]
      `(let [~delay-sym (delay ~expr)]
         (symbol-macrolet [~sym (deref ~delay-sym)]
           ~@(if (empty? more-bindings)
               body
               `[(lay ~more-bindings ~@body)])))))
Re:Programming paradigms that change how people think about ... by kazinator on Friday March 08, 2019 @02:36PM (#19341361)
That expansion strategy won't work in TXR Lisp, because if we have

  (let ((#:g1 (delay y)))
    (symacrolet ((x #:g1))
       (let ((#:g2 (delay z)))
          (symacrolet ((y #:g2))
             ...))))
the (delay y) expression is not in the scope of (symacrolet ((y #:g2)) ...). Local symbol macros are lexically scoped. They can shadow lexical variables and be shadowed by them and so on.

So y in (delay y) will not be expanded there as we need it to, which we need because the initializing expressions in mlet can refer to all the variables (including their own, if they are careful).

This is why the code binds all of the gensyms first, and then initializes them by assignment in a scope in which the symacrolets are all visible.

Using mlet plus some lazy structure like a lazy cons (lcons) we can do useful things:

  1> (set *print-circle* t)
  t
  2> (mlet ((lc (lcons 1 lc))) lc)  ;; make a circular list
  #1=(1 . #1#)
Re:Programming paradigms that change how people think about ... by kazinator on Thursday March 07, 2019 @08:52PM (#19334483)
This is not parallelized, but the blueprint is there.

Suppose we replace delay with an async opreator, and force with a corresponding await ...

The mlet macro is simple enough to be written in ... less than forty lines of readable C.

http://www.kylheku.com/cgit/txr/tree/eval.c?id=txr-212#n4217