F-Expression library

Discussion of Common Lisp

F-Expression library

Postby talexiad » Sun Apr 17, 2016 6:30 pm


I have created an f-expression library for Common Lisp. It does not emulate the f-expressions of Maclisp but attempts to offer similar flexibility.

When creating an f-expression, the programmer has access to its source code and can alter it with other functions or f-expressions. My primary goal was to emulate the expressive power of Mathematica where the source code for the user-defined functions is available in symbolic format and can be altered by other functions.

An f-expression is created using the defexpr macro. It has similar syntax with defun. It uses the symbol-value of the symbol to store the source code of the function. A symbol can be checked if it is an f-expression using f-expr. It can be evaluated and used like a normal function with feval/fapply or ffuncall. It can even be compiled for speed using compile-fexpr. A compiled f-expr will have its source code as its symbol-value and the compiled function as its symbol-function. If another function alters its source code, stored in its symbol-value, it can be compiled again.

Example of use:

(defexpr fib (n)
(cond ((eq n 0) 0)
((eq n 1) 1)
((> n 1) (+ (fib (- n 1)) (fib (- n 2))))))

CL-USER> fib
(COND ((EQ N 0) 0) ((EQ N 1) 1) ((> N 1) (+ (FIB (- N 1)) (FIB (- N 2))))))

CL-USER> (f-expr fib)
CL-USER> (feval '(fib 15))
CL-USER> (ffuncall 'fib 15)
CL-USER> CL-USER> (compile-fexpr 'fib)
CL-USER> (fib 15)
CL-USER> (disassemble 'fib)
; disassembly for (LABELS FIB)
; Size: 197 bytes. Origin: #x10059CB892
; 892: 498B4C2460 MOV RCX, [R12+96] ; thread.binding-stack-pointer
; no-arg-parsing entry point
; 897: 48894DF8 MOV [RBP-8], RCX
; 89B: B800000000 MOV EAX, 0
; 8A0: 483945F0 CMP [RBP-16], RAX
; 8A4: 0F84A3000000 JEQ L3
; 8AA: B802000000 MOV EAX, 2
; 8AF: 483945F0 CMP [RBP-16], RAX
; 8B3: 0F848D000000 JEQ L2
; 8B9: 488B55F0 MOV RDX, [RBP-16]
; 8BD: BF02000000 MOV EDI, 2
; 8C2: B930040020 MOV ECX, 536871984 ; GENERIC->
; 8C9: 7F0B JNLE L1
; 8CB: BA17001020 MOV EDX, 537919511
; 8D0: L0: 488BE5 MOV RSP, RBP
; 8D3: F8 CLC
; 8D4: 5D POP RBP
; 8D5: C3 RET
; 8D6: L1: 488B55F0 MOV RDX, [RBP-16]
; 8DA: BF02000000 MOV EDI, 2
; 8DF: 41BB40020020 MOV R11D, 536871488 ; GENERIC--
; 8E5: 41FFD3 CALL R11
; 8E8: 488BCD MOV RCX, RBP
; 8EB: 488D4424F0 LEA RAX, [RSP-16]
; 8F0: 4883EC28 SUB RSP, 40
; 8F4: 488950F0 MOV [RAX-16], RDX
; 8F8: 488908 MOV [RAX], RCX
; 8FB: 488BE8 MOV RBP, RAX
; 8FE: E88CFFFFFF CALL #x10059CB88F
; 903: 488955E8 MOV [RBP-24], RDX
; 907: 488B55F0 MOV RDX, [RBP-16]
; 90B: BF04000000 MOV EDI, 4
; 910: 41BB40020020 MOV R11D, 536871488 ; GENERIC--
; 916: 41FFD3 CALL R11
; 919: 488BCD MOV RCX, RBP
; 91C: 488D4424F0 LEA RAX, [RSP-16]
; 921: 4883EC28 SUB RSP, 40
; 925: 488950F0 MOV [RAX-16], RDX
; 929: 488908 MOV [RAX], RCX
; 92C: 488BE8 MOV RBP, RAX
; 92F: E85BFFFFFF CALL #x10059CB88F
; 934: 488BFA MOV RDI, RDX
; 937: 488B55E8 MOV RDX, [RBP-24]
; 93B: 41BBD0010020 MOV R11D, 536871376 ; GENERIC-+
; 941: 41FFD3 CALL R11
; 944: EB8A JMP L0
; 946: L2: BA02000000 MOV EDX, 2
; 94B: EB83 JMP L0
; 94D: L3: 31D2 XOR EDX, EDX
; 954: 0F0B10 BREAK 16 ; Invalid argument count trap

Anastasios Alexiadis
PhD in Applied Informatics

;;;; F-expressions for Common Lisp

;;; An f-expression
;;; Based on the anaphoric lambda from "On Lisp" by Paul Graham
(defmacro fexpr (name params &body body)
(if (null params)
`(labels ((,name () ,@body))
`(labels ((,name ,params ,@body))

;;; Remove all quotes from an s-expression
(defun unquote (exppr)
(cond ((atom exppr) exppr)
((eq (first exppr) 'quote) (unquote (eval exppr)))
((typep exppr 'cons) (mapcar #'unquote exppr))))

;;; Apply for f-expressions
(defun fapply (f-expr args)
(apply (eval (symbol-value f-expr)) args))

;;; Eval for f-expressions
(defun feval (f-expr)
(if (null (rest f-expr))
(funcall (eval (symbol-value (first f-expr))))
(apply (eval (symbol-value (first f-expr))) (rest f-expr))))

;;; Funcall for f-expressions
(defun ffuncall (f-expr &rest args)
(feval (cons f-expr args)))

;;; Compile f-expressions. When compiled they can be used as normal functions.
(defmacro compile-fexpr (f-expr)
`(setf (symbol-function ,f-expr) (eval (symbol-value ,f-expr))))

;;; Check if an s-expression is an f-expression
(defun f-expr (f-expr)
(eq (first f-expr) 'fexpr))

;;; Define an f-expression
(defmacro defexpr (name arg-list &body body)
(unless (symbolp name)
(error "The f-expression name ~S is not a symbol." name))
(when (special-operator-p name)
(error "The special operator ~S can't be redefined as an f-expression."
`(setf ,name
'(fexpr ,name ,arg-list
Posts: 1
Joined: Sun Apr 17, 2016 6:14 pm

Return to Common Lisp

Who is online

Users browsing this forum: No registered users and 8 guests