Sharad
Sharad

Reputation: 437

Are there whole-program-transforming macros in Lisp or Scheme?

I have seen one answer of How does Lisp let you redefine the language itself? Stack Overflow question (answered by Noah Lavine):

Macros aren't quite a complete redefinition of the language, at least as far as I know (I'm actually a Schemer; I could be wrong), because there is a restriction. A macro can only take a single subtree of your code, and generate a single subtree to replace it. Therefore you can't write whole-program-transforming macros, as cool as that would be.

After reading this I am curious about whether there are "whole-program-transforming macros" in Lisp or Scheme (or some other language).

If not then why?

Update

One kind of use case e.g.

As in stumpwm code here are some functions all in different lisp source files uses a dynamic/global defvar variable *screen-list* that is defined in primitives.lisp , but used in screen.lisp, user.lisp, window.lisp. (Here each files have functions, class, vars related to one aspect or object)

Now I wanted to define these functions under the closure where *screen-list* variable available by let form, it should not be dynamic/global variable, But without moving these all functions into one place (because I do not want these functions to lose place from their related file) So that this variable will be accessible to only these functions.

Above e.g. equally apply to label and flet, so that it will further possible that we could make it like that only required variable, function will be available, to those who require it.

Note one way might be implement and use some macro defun_with_context for defun where first argument is context where let, flet variables definend. But apart from it could it be achieved by reader-macro as Vatine and Gareth Rees answered.

Upvotes: 6

Views: 1014

Answers (7)

Sam Tobin-Hochstadt
Sam Tobin-Hochstadt

Reputation: 5053

In Racket, you can implement whole-program-transforming macros. See the section in the documentation about defining new languages. There are many examples of this in Racket, for example the lazy language and Typed Racket.

Upvotes: 5

WReach
WReach

Reputation: 18271

In Common LISP, at least, you may wrap top-level forms in PROGN and they still retain their status as top-level forms (see CLTL2, section 5.3). Therefore, the limitation of a macro generating a single subtree is not much of a limitation since it could wrap any number of resulting subtrees within PROGN. This makes whole-program macros quite possible.

E.g.

(my-whole-program-macro ...)

= expands to =>

(progn
  (load-system ...)
  (defvar ...)
  (defconstant ...)
  (defmacro ...)
  (defclass ...)
  (defstruct ...)
  (defun ...)
  (defun ...)
  ...
  )

Upvotes: 0

Paul Nathan
Paul Nathan

Reputation: 40299

I believe those sorts of macros are called code-walking macros. I haven't implemented a code walker myself, so I am not familiar with the limits.

Upvotes: 0

Ken
Ken

Reputation: 756

Off the top of my head, a few approaches:

First, you can. Norvig points out that:

We can write a compiler as a set of macros.

so you can transform an entire program, if you want to. I've only seen it done rarely, because typically the intersection between "things you want to do to every part of your program" and "things that you need macro/AST-type transformations for" is a pretty small set. One example is Parenscript, which transforms your Lisp code ("an extended subset of CL") into Javascript. I've used it to compile entire files of Lisp code into Javascript which is served directly to web clients. It's not my favorite environment, but it does what it advertises.

Another related feature is "advice", which Yegge describes as:

Great systems also have advice. There's no universally accepted name for this feature. Sometimes it's called hooks, or filters, or aspect-oriented programming. As far as I know, Lisp had it first, and it's called advice in Lisp. Advice is a mini-framework that provides before, around, and after hooks by which you can programmatically modify the behavior of some action or function call in the system.

Another is special variables. Typically macros (and other constructs) apply to lexical scope. By declaring a variable to be special, you're telling it to apply to dynamic scope (I think of it as "temporal scope"). I can't think of any other language that lets you (the programmer) choose between these two. And, apart from the compiler case, these two really span the space that I'm interested in as a programmer.

Upvotes: 2

Anthony
Anthony

Reputation: 3791

A typical approach is to write your own module system. If you just want access to all the code, you can have some sort of pre-processor or reader extension wrap source files with your own module annotation. If you then write your own require or import form, you will ultimately be able to see all the code in scope.

To get started, you could write your own module form that lets you define several functions which you then compile in some clever way before emitting optimized code.

Upvotes: 1

Vatine
Vatine

Reputation: 21258

There's always the choice of using compiler macros (they can do whole-function transformation based on a lew of criteria, but shouldn't change the value returned, as that would be confusing).

There's reader macros, they transform the input "as it is read" (or "before it is read", if you prefer). I haven't done much large-scale reader-macro hacking, but I have written some code to allow elisp sourec to be (mostly) read in Common Lisp, with quite a few subtle differences in syntactic sugar between the two.

Upvotes: 0

Gareth Rees
Gareth Rees

Reputation: 65854

You quoted Noah Lavine as saying:

A macro can only take a single subtree of your code, and generate a single subtree to replace it

This is the case for ordinary macros, but reader macros get access to the input stream and can do whatever they like with it.

See the Hyperspec section 2.2 and the set-macro-character function.

Upvotes: 5

Related Questions