Skip to content

Instantly share code, notes, and snippets.

@Wilfred
Last active December 17, 2015 13:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wilfred/5615907 to your computer and use it in GitHub Desktop.
Save Wilfred/5615907 to your computer and use it in GitHub Desktop.
toying with TCO in elisp
;; tco.el --- tail-call optimisation -*- lexical-binding: t -*-
(require 'dash)
(eval-when-compile (require 'cl))
(setq lexical-binding 't)
(defun tco-add-trampoline (fun-name new-name form)
"Given quoted soure FORM, replace calls to FUN-NAME (a symbol)
with a lambda expression that returns the result of the FUN-NAME call."
(--map
(cond
((consp it)
(if (eq (car it) fun-name)
`(lambda () (,new-name ,@(cdr it)))
(tco-add-trampoline fun-name new-name it)))
('t it))
form))
;; todo: error if not in tail position
;; todo: macro-expand function body first
;; todo: preserve function arity to improve byte-compiler warnings
;; todo: docstring support
(defmacro defun-tco (function-name args &rest body)
(let* ((name (make-symbol "trampolined-function"))
(trampolined
(tco-add-trampoline function-name name body))
(fun-args (make-symbol "outer-fun-args"))
(result (make-symbol "trampolined-result")))
`(defun ,function-name (&rest ,fun-args)
(flet ((,name ,args ,@trampolined))
(let ((,result (apply ',name ,fun-args)))
(while (functionp ,result)
(setq ,result (funcall ,result)))
,result)))))
;; example usage
(defun-tco fact (x &optional accum)
(setq accum (or accum 1))
(if (eql x 1) accum
(fact (1- x) (* accum x))))
@Wilfred
Copy link
Author

Wilfred commented May 25, 2013

This now lives at https://github.com/Wilfred/tco.el .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment