(chapter-start ‘flow-control “constructs for looping and error management”)
(mac on-err (handler . body)
; executes 'body. If an error is raised, executes 'handler. Inside ; 'handler, the parameter 'errors is a list of error messages extracted from ; the sequence of errors that led here (Exception#cause in ruby or Throwable.getCause() in java) `(handle-error (fn (errors traces) ,handler) (fn () ,@body)))
;; executes ‘body. Afterwards, executes ’protection. ;; ‘protection is always executed even if there is an error. (mac ensure (protection . body)
`(ensuring (fn () ,protection) (fn () ,@body)))
;; tests ‘test, as long as ’test is non-nil, ;; repeatedly executes ‘body (mac while (test . body)
`(loop ,test (do ,@body)))
;; execute ‘start, then for as long as ’test returns non-nil, ;; execute ‘body and ’update (mac looping (start test update . body)
`(do ,start (while ,test ,@body ,update)))
;; assign ‘init to ’v, then execute ‘body ’max times, ;; incrementing ‘v at each iteration (mac for (v init max . body)
(w/uniq (gi gm) `(with (,v nil ,gi ,init ,gm (+ ,max 1)) (looping (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1)) ,@body))))
;; return a new function which is the original function with ;; the given args1 already applied ;; arguments to the new function are whatever arguments remain ;; for the old function ;; Could be (mac curry things ‘(fn args (apply ,@things args))) but less readable (mac curry (f . args0)
`(fn args (apply ,f ,@args0 args)))
;; like curry, but the returned function takes only a single arg (assumes all ;; args but one are provided here) ;; Could be (mac curry1 things ‘(fn (arg) (,@things arg))) but less readable (mac curry1 (f . args)
`(fn (arg) (,f ,@args arg)))
;; if ,key is already in ,hsh - return the associated value. ;; if ,key is not already in ,hsh - evaluate ,val, store the result ;; under ,key in ,hsh, and return it (mac cache-get (hsh key val)
(w/uniq (h k) `(with (,h ,hsh ,k ,key) (or= (hash-get ,h ,k) ,val))))
;; same as ‘def, but caches the result, keyed on args, so for a given set of args the result ;; is only ever calculated once ;; ;; WARNING: in current incarnation, won’t work with destructuring args (mac defmemo (name args . body)
(let forms (filter-forms (build-def-hash) body) (w/uniq h `(let ,h (hash) (def ,name ,args ,@(map (fn (c) (cons 'comment c)) forms.comment) ,@(map (fn (c) (cons 'chapter c)) forms.chapter) (cache-get ,h (list ,@args) (do ,@(hash-get forms nil))))))))
;; memoises a function expression ;; args: the function arguments ;; body: a list of function body expressions ;; next: a function to assemble a function expression from ‘args and ’body ;; returns whatever ‘next returns, where ’body is memoised based on the value of ‘args (def memoise (args body next)
(let (memo newbody) (filter-remove '#memoise body) (if memo (w/uniq h `(let ,h (hash) ,(next args `((cache-get ,h (list ,@args) (do ,@newbody)))))) (next args body))))
(assign fun/expanders
(cons (cons 'memoise memoise) fun/expanders))