Last active
November 2, 2015 10:40
-
-
Save StephenWakely/d64aec2b48aad30f317c to your computer and use it in GitHub Desktop.
Logging monad in Common Lisp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defun mkStr (&rest args) | |
(with-output-to-string (s) | |
(dolist (a args) (princ a s)))) | |
(defclass Out () | |
((output :accessor output | |
:initarg :output) | |
(result :accessor result | |
:initarg :result))) | |
(defun return-out (result) | |
(make-instance 'Out :output "" :result result)) | |
(defmethod >>= ((out Out) fn) | |
(with-slots ((x result) (ox output)) out | |
(with-slots ((oy output) (y result)) (funcall fn x) | |
(make-instance 'Out :output (mkStr ox oy) :result y)))) | |
(defun line (num total) | |
(make-instance 'Out :output (mkStr "Number is :" num " ") :result total)) | |
(defmacro doM (stmt &rest stmts) | |
"do macro (do is reserved in lisp, so it is called doM instead)" | |
(let* ((is-bound (eq '<- (second stmt))) | |
(param (if is-bound (first stmt) (gensym))) | |
(stmt* (if is-bound (third stmt) stmt))) | |
(if stmts | |
`(>>= ,stmt* (lambda (,param) | |
,@(when (not is-bound) | |
`((declare (ignore ,param)))) | |
(doM ,@stmts))) | |
stmt*))) | |
(defun add-a-list (&rest nums) | |
"Add together the numbers in a list using the Out monad to | |
log each of the numbers being added together. Uses the raw | |
bind (>>=) functions to pass the monad along - this gets long | |
winded pretty quickly. The doM macro makes things much clearer. | |
Observe add-a-list-do." | |
(if nums | |
(>>= | |
(apply #'add-a-list (rest nums)) | |
(lambda (total) | |
(>>= | |
(line (first nums) total) | |
(lambda (um) | |
(declare (ignore um)) | |
(return-out (+ (first nums) total)))))) | |
(return-out 0))) | |
(defun add-a-list-do (&rest nums) | |
"Same as above using the do macro instead" | |
(if nums | |
(doM | |
(total <- (apply #'add-a-list-do (rest nums))) | |
(line (first nums) total) | |
(return-out (+ (first nums) total))) | |
(return-out 0))) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment