Over the weekend I needed to set up a R6RS Scheme interpreter on Cygwin. It came down to either PLT or Ikarus. Both seem to be straightforward builds but I couldn’t make PLT happy so I went with Ikarus instead. It was a very simple and straightforward configuration and took maybe a minute to build. Once things were clearly working fine I figured I would try to get some of Ed’s OpenGL (box2d-lite and agave) demos running just for the fun of it.
Both of the programs depend on the GL and GLUT libraries. At runtime the correct DLL is loaded depending on the OS type. There wasn’t a setting for Cygwin so I added one. The thing was that I specified the wrong file name: /usr/lib/libGL.dll.a and /usr/lib/libglut.dll.a.
It was an uneducated guess in the first place. I figured there would be a one to one mapping. After Marco kindly kicked my butt on the Ikarus list though, by asking some basic questions like has it ever worked and have I checked out the difference in error messages, I got me thinking that I should have read up on this.
The Cygwin documentation here and the Redhat documentation here seem to explain it… Windows DLLs need additional information to be linked against. On Cygwin, when GCC sees .dll.a files it “knows” how to get the additional data out of them in case you want to link to Win32. Reading on in the Redhat documentation, it lists the DLL search path when you specify -L argument for GCC. In that list I saw that /bin is included. That surprised me.
It turns out that on Cygwin, DLLs that are not compiled to work with Win32 are located there. At least, this is my understanding. When you link to these DLLs though, the OpenGL demos work just fine on Cygwin with Ikarus.
Is this also your understanding? I need to dig in more to this topic.
I had been trying to get so many things working this weekend that I didn’t invest the amount of the time that this deserved, or most of those things for that matter.
Aziz added the ability to change the REPL prompt in the latest revision of Ikarus:
I’ve started working on a package manager implementation, dubbed “dorodango”. I post this as a feedback request, not because there’s working code to show yet.
dorodango is/will (ATM) be based upon these concepts, which I hereby put up for discussion:
Read the rest here.
In this post, Aziz explains the history of how he went about bootstrapping Ikarus Scheme.
First, there was a Scheme->C compiler (running under Petite Chez). I
did not invest much in making it generate good code (details omitted)
since I know I was going to throw it away eventually. I would use it
to compile some minimal scheme runtime system (the usual R5RS stuff)
plus an interpreter (straight-forward environment-passing,
continuation-passing interpreter). The result of compiling this was
a huge C file that GCC took ages to compile, but eventually, you get
a repl that can interpret code (slowwwly). These were not good times.
Next, I wrote an in-core assembler: it can allocate a chunk of memory,
whack it with some powerpc instructions, and jump to it. I of course
did not compile the assembler, because any piece of code added to the
base system that was compiled to C was adding to the ages it took GCC
to compile. So, the assembler ran interpreted, but the code it makes
can be executed from the interpreter.
Next, I wrote the in-core compiler that uses the in-core assembler
but can compile Scheme code. So, you can compile a Scheme procedure,
and you can call it from the interpreter, and it can call other
compiled procedures, but it cannot call interpreted code, nor can it
call C code. So, once you call a compiled procedure, it’s in another
universe until it returns. The compiler is still running interpreted
(under a bad interpreter too) so it was very very very slow, but the
code it generated was good (if it worked that is! the system was
still being debugged).
Next, I managed to compile the entire runtime system including the
interpreter, the assembler, and the compiler itself.
So, at that point, to compile ikarus from scratch, you first compile
a minimal runtime system and an interpreter to C, then you use the
interpreter to load the assembler and compiler (and some additional
runtime that I could not compile), then you use the interpreted
compiler to compile the run-time and assembler and compiler and some
startup code (initialization), then you execute the initialization
and lo and behold, you’re running in the compiler!
IIRC, this process (if successful) took at least an hour, so, I was
not eager to do it frequently, and this is why I “invented” saved
code: so that I can compile everything, and save the initialization
code along with everything reachable from it and so that next time,
I can just load it and be immediately in the compiler.
Notice that running in the compiler was not (semantically) different
from running in the interpreter: it was only much faster since you
are not being interpreted by a poorly-written interpreter that was
compiled poorly to C. So, bootstrapping was now much faster since
I can load the already compiled system, compile everything, then
save the system. As that got more reliable, the interpreter and
Scheme->C compiler were not being used for anything other than for
loading and jumping to the pre-compiled code, so, I wrote a little
piece of C code that does just that and threw the rest away! At
the end, bootstrap time became 20~40 seconds on the 800MHz iBook
down from an hour or so. It was finally good times.
And that’s how that incarnation of ikarus was born.
In this post Aziz announced that Ikarus has preliminary debugger support. Awesome.
An R6RS library and a Chez top-level module have similarities: they both have bodies that contain variable and syntax definitions, some of which can be exported, and you can import one module into another. So, they’re similar on the surface. But there are major differences.
1. Outer scope
Chez modules exist in some environment (typically, in the interaction-environment, but that can change) that contains bindings that include the module keyword itself. The outer scope in the environment in which the module is expanded is visible inside the module (unless you restrict it using the import-only keyword). R6RS/Ikarus libraries do not exist in an environment: they are stand-alone entities with their own syntax and so on.
2. Expansion and Evaluation
Ikarus libraries are automatically expanded/compiled when you import them (thus, there’s a mapping from library names to some storage that contains libraries’ code). Chez modules are not associated with any storage: if you import a module and it doesn’t already exist in the environment, you get an error. This means that in Chez, it is your duty to ensure that modules are “loaded” before they are used. Now “loaded” is a loaded term as it implies all of “compiled”, “visited”, and “invoked” (or “revisited” in the Chez lingua). Moreover, the load, compile, visit, and revisit are operations that operate on files, not modules, (such files may contain any code, not just modules) and work by modifying the environment in which such files are loaded, compiled, visited, or revisted. Operations on libraries in Ikarus do not modify the environment since no environment is required for performing these operations. In short, you need to manage the phase dependencies (when files are loaded/compiled/visited/revisited) yourself so that compile-time and run-time information are available when needed.