Showing posts with label edgelisp. Show all posts
Showing posts with label edgelisp. Show all posts

Saturday, May 14, 2011

Secret toplevel

A somewhat arcane aspect of hygienic macro systems is the notion that macro-introduced toplevel identifiers are secret.

An example of this in EdgeLisp:

The macro FOO expands to code that defines a variable X using DEFVAR, and prints its value:

(defmacro foo () #'(progn (defvar x 1) (print x)))

(#' is EdgeLisp's code quotation operator.)

Using the macro has the expected effect:

(foo)
1

But X isn't actually a global variable now:

x
; Condition: The variable x is unbound.
; Restarts:
; 1: #[handler [use-value]]
; 2: #[handler [retry-repl-request]]
; 3: #[handler [abort]]

The name X only makes sense inside the expansion of (foo). Outside, it gets transparently renamed by the hygienic macro system.

(In EdgeLisp, a UUID is attached as color (or hygiene context) to the identifier.)

Why the secrecy of toplevel variables? Well, it's simply an extension of the notion that identifiers that introduce new variable bindings (such as those in a LET) need to be treated specially to prevent unhygienic name clashes between macro- and user-written code. This secrecy completely frees macro writers from having to care about the identifiers they choose.

(Discussion of this topic on the R7RS list: Are generated toplevel definitions secret?)

Tuesday, May 10, 2011

Hygiene in EdgeLisp

EdgeLisp is chugging along nicely. I'll soon do a proper release.

I just implemented a variant of SRFI 72, that is a hygienic defmacro. (I wrote two articles about SRFI 72: part 1 and part 2).
(defmacro swap (x y)
#`(let ((tmp ,x))
(setq ,x ,y)
(setq ,y tmp)))
nil
(defvar x 1)
1
(defvar tmp 2)
2
(swap x tmp)
1
x
2
tmp
1
(swap tmp x)
1
x
1
tmp
2

Sunday, May 8, 2011

Compiling, linking, and loading in EdgeLisp

EdgeLisp now has first-class object files (called FASL, for Fast-Loadable) and a primitive linker:

Let's compile a FASL from a piece of code:

(defvar fasl (compile #'(print "foo")))
#[fasl [{"execute":"((typeof _lisp_function_print !== \"undefined\" ? _lisp_function_print : lisp_undefined_identifier(\"print\", \"function\", undefined))(null, \"foo\"))"}]]

(Note that EdgeLisp uses #' and #` for code quotation.)

The FASL contains the compiled JavaScript code of the expression (print "foo").

We can load that FASL, which prints foo.

(load fasl)
foo
nil

Let's create a second FASL:

(defvar fasl-2 (compile #'(+ 1 2)))
#[fasl [{"execute":"((typeof _lisp_function_P !== \"undefined\" ? _lisp_function_P : lisp_undefined_identifier(\"+\", \"function\", undefined))(null, (lisp_number(\"+1\")), (lisp_number(\"+2\"))))"}]]

(load fasl-2)
3

Using link we can concatenate the FASLs, combining their effects:

(defvar linked-fasl (link fasl fasl-2))
(load linked-fasl)
foo
3

Compile-time effects

Fasls keep separate the runtime and the compile-time effects of code.

For example, a macro definition returns nil at runtime, and does its work at compile-time (edited for readability):

(compile #'(defmacro foo () #'bar))
#[fasl [

execute:
(typeof _lisp_variable_nil !== "undefined" ? _lisp_variable_nil : lisp_undefined_identifier("nil", "variable", undefined))

compile:
((typeof _lisp_function_Nset_macro_function !== "undefined" ? _lisp_function_Nset_macro_function : lisp_undefined_identifier("%set-macro-function", "function", undefined))(null, "foo", (function(_key_, _lisp_variable_NNform){ lisp_arity_min_max(arguments.length, 2, 2); return (((typeof _lisp_function_Ncompound_apply !== "undefined" ? _lisp_function_Ncompound_apply : lisp_undefined_identifier("%compound-apply", "function", undefined))(null, (function(_key_){ lisp_arity_min_max(arguments.length, 1, 1); return (((new Lisp_identifier_form("bar")))); }), ((typeof _lisp_function_Ncompound_slice !== "undefined" ? _lisp_function_Ncompound_slice : lisp_undefined_identifier("%compound-slice", "function", undefined))(null, (typeof _lisp_variable_NNform !== "undefined" ? _lisp_variable_NNform : lisp_undefined_identifier("%%form", "variable", undefined)), (lisp_number("+1"))))))); }))) ]]

Thursday, April 21, 2011

EdgeLisp progress

My hobby Lisp->JS compiler, EdgeLisp (formerly known as CyberLisp) just passed a major milestone: using multiple dispatch, a numeric tower (provided by Danny Yoo's js-numbers), and inline JavaScript, I'm able to write the > generic function as follows:
(defgeneric > (a b))
(defmethod > ((a number) (b number))
#{ jsnums.greaterThan(~a, ~b) #})
Checking it out at the REPL:
(> 200000000000000000000000000000000000000000 100000000000000000000000000000000000000000)
#t
Yay!

EdgeLisp is FAR from usable by anyone but me, but at this point I just had to blog about it. You can check it out here if you want.

Disclaimer: docs are outdated, and the project is in rapid flux.

The more I work on this, the more I respect anybody who's produced a usable PL. The sheer amount of work is unbelievable. :)

Props also to Douglas Crockford's json.js and Chris Double's jsparse.