Skip to content

Instantly share code, notes, and snippets.

@weird-ego
Created August 24, 2020 14:14
Show Gist options
  • Save weird-ego/ce704cbf687ae19036f22fbef6053620 to your computer and use it in GitHub Desktop.
Save weird-ego/ce704cbf687ae19036f22fbef6053620 to your computer and use it in GitHub Desktop.
CL implementation of fn with dispatch against arguments definition pattern. Inspired by @andreyorst.
(defun any-of (l)
(and (not (null l))
(or (car l)
(any-of (cdr l)))))
(defun init (l)
(if (or (= (length l) 1) (null l))
nil
(cons (car l) (init (cdr l)))))
(defmacro zip-let (names arguments code)
``(funcall (lambda (,@names) ,@code) ,@arguments))
(defmacro zip-let-2
(names arguments code)
`(let* ((rest-arg (last arguments))
(arguments (init arguments))
(rest-name (car (last names)))
(names (init (init names))))
`(funcall (lambda (,@names ,rest-name) ,@code)
,@arguments (list ,@rest-arg))))
(defmacro dispatch (arguments clauses)
`(let ((arguments ,arguments))
(map 'list
(lambda (pair)
(let ((names (car pair))
(code (cdr pair)))
(cond ((and (find '& names) (= (- (length names) 1) (length arguments)))
(zip-let-2 names arguments code))
((= (length names) (length arguments))
(zip-let names arguments code))
(t nil))
))
,clauses)))
(defmacro fn-match (name none-given &rest clauses)
`(defun ,name (&rest arguments)
(let* ((matches (dispatch arguments (list ,@clauses)))
(match (any-of matches)))
(cond
(match (eval match))
((null arguments) (eval ,none-given))
(t (error "WRONG USAGE"))))))
(defvar code
'(fn-match vaiv
:zero
'((a) (list :one a))
'((a b) (list :first a :second b))
'((a b & c) (list :first a :second b :rest c))))
(eval code)
(print (vaiv))
(print (vaiv 1))
(print (vaiv 1 2))
(print (vaiv 1 2 3))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment