The CHICKEN FAQ

Questions:

  1. Why yet another Scheme implementation?
  2. How do I generate a DLL under Windows(tm)?
  3. How do I run custom startup code before the runtime-system is invoked?
  4. What is the meaning of the -hygienic option?
  5. Why does (define-macro foo bar) not work?
  6. Why doesn't my fancy macro work in compiled code?
  7. What to do if I find a bug?
  8. Why are values defined with define-foreign-variable or define-constant or define-inline not seen outside of the containing source file?
  9. How does cond-expand know which features are registered in used units?
  10. How can I cut down the size of an executable?
  11. Why are macros not visible outside of the compilation unit in which they are defined?
  12. How do I generate a GUI application under MS Windows (tm)?
  13. Why does a loop that doesn't cons still trigger garbage collections?
  14. How can I obtain faster executables?
  15. Why does the linker complain about a missing function _C_..._toplevel?
  16. Why does the linker complain about a missing function _C_toplevel?
  17. Can I run the compiler in the interpreter?
  18. Why are constants defined by define-constant not honoured in case constructs?
  19. When I compile a file with -unsafe or unsafe declarations, it crashes during execution.
  20. How can I add compiled user passes?
  21. Which non-standard procedures are treated specially when the extended-bindings or usual-integrations declaration or compiler option is used?
  22. Why does define-reader-ctor not work in my compiled program?
  23. Why does my program abort with an "out of memory" error when I compile with extended debug information?
  24. Why do I get a warning when I define a global variable named match?
  25. How can I enable case sensitive reading/writing in user code?
  26. When I use callback functions (from Scheme to C and back to Scheme again), I get weird crashes.
  27. When I run csi inside an emacs buffer under Windows, nothing happens.
  28. How can I change match-error-control during compilation?
  29. Why doesn't CHICKEN support the full numeric tower?
  30. How can I put toplevel definitions inside eval-when in combination with the highlevel macro system?
  31. Compiling very large files under Windows with the Microsoft C compiler fails with a message indicating insufficient heap space.
  32. Toplevel-continuations captured in interpreted code don't seem to work.
  33. How can I specialize a generic function method to match instances of every class?
  34. I use SWIG to create FFI wrappers, and my application seems to be caught in an infinite loop for no apparent reason.
  35. Why does a chicken have both dark and white meats?

Answers:

  1. Why yet another Scheme implementation?

    Since Scheme is a relatively simple language, a large number of implementations exist and each has its specific advantages and disadvantages. Some are fast, some provide a rich programming environment. Some are free, others are tailored to specific domains, and so on. The reasons for the existance of CHICKEN are:

    • CHICKEN is simple. It can be used as a pedagogical tool for anybody who is interested in the workings of a compiler.
    • CHICKEN is portable because it generates C code that runs on a large number of platforms.
    • CHICKEN is extendable, since its code generation scheme and runtime system/garbage collector fits neatly into a C environment.
    • CHICKEN is free and can be freely distributed, including its source code.
    • CHICKEN offers better performance than nearly all interpreter based implementations, but still provides full Scheme semantics.
    • As far as I know, CHICKEN is the first implementation of Scheme that uses Henry Baker's Cheney on the M.T.A concept.

  2. How do I generate a DLL under MS Windows (tm) ?

    Use csc in combination with the -dll option:

       C:\> csc foo.scm -dll
    
    You can use the define-entry-point facility to interface to the Scheme code (see the User's manual).
  3. How do I run custom startup code before the runtime-system is invoked?

    When you invoke the C compiler for your translated Scheme source program, add the C compiler option -DC_EMBEDDED, or pass -embedded to the csc driver program, so no entry-point function will be generated (main()). When your are finished with your startup processing, invoke:

       CHICKEN_main(argc, argv, C_toplevel);
    
    where C_toplevel is the entry-point into the compiled Scheme code. You should add the following declarations at the head of your code:
      #include "chicken.h"
      extern void C_toplevel(C_word,C_word,C_word) C_noret;
    
    See the manual for more information.
  4. What is the meaning of the -hygienic option?

    The high-level macro system adds some startup overhead to the system. Using this options evaluates some standard macro-definitions and will slow down the startup-times of the compiler and the interpreter. For many situations the simple define-macro style macro-system is sufficient, and so the use of the extended macro system is optional. If you prefer that the compiler or interpreter use the syntax-case macro- system by default, consider setting the environment variables CHICKEN_OPTIONS and/or CSI_OPTIONS to -hygienic.


  5. Why does (define-macro foo bar) not work?

    Consider this code snippet:

       (define (double x) (list '* x x))
    
       (define-macro twice double)
    
    Because the macro twice is defined at compile time (following forms may refer to it), functions defined at runtime (as double in this case) are not available. The alternative syntax of define-macro:
       (define-macro twice (lambda (x) (list '* x x)))
    
    does just exist to make porting other Scheme code easier. It is not able to assign procedures computed at runtime as macro expanders.
  6. Why doesn't my fancy macro work in compiled code?

    Macro bodies that are defined and used in a compiled source-file are evaluated during compilation and so have no access to definitions in the compiled file. Note also that during compile-time macros are only available in the same source file in which they are defined. Files included via include are considered part of the containing file.


  7. What to do if I find a bug?

    Send e-mail to felix@call-with-current-continuation.org with some hints about the problem, like version/build of the compiler, platform, system configuration, code that causes the bug, etc.


  8. Why are values defined with define-foreign-variable or define-constant or define-inline not seen outside of the containing source file?

    Accesses to foreign variables are translated directly into C constructs that access the variable, so the Scheme name given to that variable does only exist during compile-time. The same goes for constant- and inline-definitions: The name is only there to tell the compiler that this reference is to be replaced with the actual value.


  9. How does cond-expand know which features are registered in used units?

    Each unit used via (declare (uses ...)) is registered as a feature and so a symbol with the unit-name can be tested by cond-expand during macro-expansion-time. Features registered using the register-feature! procedure are only available during run-time of the compiled file. You can use the eval-when form to register features at compile time.


  10. How can I cut down the size of an executable?

    If you don't need eval or the stuff in the extras library unit, you can just use the library unit:

    	(declare (uses library))
    	(display "Hello, world!\n")
    
    (Don't forget to compile with the -explicit-use option) Compiled with Visual C++ this generates an excutable of around 240 kilobytes. It is theoretically possible to compile something without the library, but a program would have to implement quite a lot of support code on its own.
  11. Why are macros not visible outside of the compilation unit in which they are defined?

    Macros are defined during compile time, so when a file has been compiled, the definitions are gone. An exception to this rule are macros defined with define-macro, which are also visible at run-time, i.e. in eval. To use macros defined in other files, use the include special form.


  12. How do I generate a GUI application under Windows(tm)?

    Invoke csc with the -windows option. Or pass the -DC_WINDOWS_GUI option to the C compiler and link with the GUI version of the runtime system (that's libchicken-gui[-static].lib. The GUI runtime displays error messages in a message box and does some rudimentary command-line parsing.


  13. Why does a loop that doesn't cons still trigger garbage collections?

    Under CHICKENs implementation policy, tail recursion is achieved simply by avoiding to return from a function call. Since the programs is CPS converted, a continuous sequence of nested procedure calls is performed. At some stage the stack-space has to run out and the current procedure and its parameters (including the current continuation) are stored somewhere in the runtime system. Now a minor garbage collection occurs and rescues all live data from the stack (the first heap generation) and moves it into the the second heap generation. Than the stack is cleared (using a longjmp) and execution can continue from the saved state. With this method arbitrary recursion (in tail- or non-tail position) can happen, provided the application doesn't run out of heap-space. (The difference between a tail- and a non-tail call is that the tail-call has no live data after it invokes its continuation - and so the amount of heap-space needed stays constant)


  14. How can I obtain faster executables?

    There are a number of declaration specifiers that should be used to speed up compiled files: declaring (standard-bindings) is mandatory, since this enables most optimizations. Even if some standard procedures should be redefined, you can list untouched bindings in the declaration.
    Declaring (extended-bindings) lets the compiler choose faster versions of certain internal library functions. This might give another speedup. You can also use the the usual-integrations declaration, which is identical to declaring standard-bindings and extended-bindings (note that usual-integrations is set by default).
    Declaring (block) tells the compiler that global procedures are not changed outside the current compilation unit, this gives the compiler some more opportunities for optimization.
    If no floating point arithmetic is required, then declaring (number-type fixnum) can give a big performance improvement, because the compiler can now inline most arithmetic operations.
    Declaring (unsafe) will switch off most safety checks.
    If threads are not used, you can declare (disable-interrupts).
    You should always use maximum optimizations settings for your C compiler. Good GCC compiler options on Pentium (and compatible) hardware are:

    -O3 -fomit-frame-pointer -f-nostrict-aliasing

    Some programs are very sensitive to the setting of the nursery (the first heap-generation). You should experiment with different nursery settings (either by compiling with the -nursery option or by using the -:s... runtime option).
  15. Why does the linker complain about a missing function _C_..._toplevel?

    This message indicates that your program uses a library-unit, but that the object-file or library was not supplied to the linker. If you have the unit foo, which is contained in foo.o than you have to supply it to the linker like this (assuming a GCC environment):

       % chicken program.scm -output-file program.c
       % gcc program.c foo.o `chicken-config -cflags -libs` -o program
    
    The CHICKEN runtime library (libchicken.a or libchicken.lib) already contains the units library, eval and syntax-case, plus the internally used units profiler> and scheduler. The extras library (libstuffed-chicken.a or libstuffed-chicken.lib) contains the units extras, format, lolevel, tinyclos, regex, tcp and posix (if available). The SRFI library (libsrfi-chicken.a or libsrfi-chicken.lib) contains the units srfi-1, srfi-4, srfi-13, srfi-14, srfi-18, srfi-25 and srfi-37.
  16. Why does the linker complain about a missing function _C_toplevel?

    This means you have compiled a library unit as an application. When a unit-declaration (as in (declare (unit ...))) is given, then this file has a specially named toplevel entry procedure (see Q23). Just remove the declaration, or compile this file to an object-module and link it to your application code.


  17. Can I run the compiler in the interpreter?

    To cut down turnaround times during development of the compiler, you can run it in the interpreter csi, like this:

       csi $CHICKEN_HOME/src/chicken.scm -include-path $CHICKEN_HOME/src SOURCE-FILENAME OPTION1 ...
    

  18. Why are constants defined by define-constant not honoured in case constructs?

    Case expands into a cascaded if expression, where the first item in each arm is treated as a quoted list. So the case macro can not infer wether a symbol is to be treated as a constant-name (defined via define-constant) or a literal symbol.


  19. When I compile a file with -unsafe or unsafe declarations, it crashes during execution.

    The compiler option -unsafe or the declaration (declare (unsafe)) disable certain safety-checks to improve performance, so code that would normally trigger an error will work unexpectedly or even crash the running application. It is advisable to develop and debug a program in safe mode (without unsafe declarations) and use this feature only if the application works properly.


  20. How can I add compiled user passes?

    To add a compiled user pass instead of an interpreted one, create a library unit and recompile the main unit of the compiler (in the file chicken.scm) with an additional uses declaration. Then link all compiler modules and your (compiled) extension to create a new version of the compiler, like this (assuming a UNIX like environment and also assuming all sources are in the current directory):

      % cat userpass.scm
      ;;;; userpass.scm - My very own compiler pass
    
      (declare (unit userpass))
    
      ;; Perhaps more user passes/extensions are added:
      (let ([old (user-pass)])
        (user-pass
          (lambda (x)
            (let ([x2 (do-something-with x)])
    	  (if old
    	      (old x2)
    	      x2) ) ) ) )
      ...
      % chicken userpass.scm -output-file userpass.c -explicit-use -quiet
      % chicken chicken.scm -output-file chicken-extended.c -quiet -postlude "(declare (uses userpass))"
      % gcc -c userpass.c `chicken-config -cflags` -o userpass.o
      % gcc -c chicken-extended.c `chicken-config -cflags` -o chicken-extended.o
      % gcc chicken-extended.o support.o easyffi.o compiler.o optimizer.o batch-driver.o c-platform.o \
        c-backend.o userpass.o `chicken-config -libs` -o chicken-extended
    
    On platforms that support it (Linux ELF, Solaris, Windows + VC++), compiled code can be loaded via -extend just like source files (see load in the User's Manual).
  21. Which non-standard procedures are treated specially when the extended-bindings or usual-integrations declaration or compiler option is used?

    The following extended bindings are handled specially:

    bitwise-and
    bitwise-ior
    bitwise-xor
    bitwise-not
    add1
    sub1
    fx+
    fx-
    fx*
    fx/
    fxmod
    fx=
    fx>
    fx<
    fx>=
    fx<=
    fixnum?
    fxneg
    fxmax
    fxmin
    fp+
    fp-
    fp*
    fp/
    atom?
    fp=
    fp>
    fp<
    fp>=
    fp<=
    fpneg
    fpmax
    fpmin
    arithmetic-shift
    signum
    flush-output
    thread-specific
    thread-specific-set!
    not-pair?
    null-list?
    print
    print*
    u8vector->bytevector
    s8vector->bytevector
    u16vector->bytevector
    s16vector->bytevector
    u32vector->bytevector
    s32vector->bytevector
    f32vector->bytevector
    f64vector->bytevector
    block-ref
    block-set!
    number-of-slots
    first
    second
    third
    fourth
    null-pointer?
    pointer->object
    make-record-instance
    locative-ref
    locative-set!
    locative?
    locative->object
    identity
    cpu-time
    error
    call/cc.


  22. Why does define-reader-ctor not work in my compiled program?

    The following piece of code does not work as expected:

    (eval-when (compile)
      (define-reader-ctor 'integer->char integer->char) )
    (print #,(integer->char 33))
    

    The problem is that the compiler reads the complete source-file before doing any processing on it, so the sharp-comma form is encountered before the reader-ctor is defined. A possible solution is to include the file containing the sharp-comma form, like this:

    (eval-when (compile)
      (define-reader-ctor 'integer->char integer->char) )
    
    (include "other-file")
    

    "other-file.scm":

    (print #,(integer->char 33))
    

  23. Why does my program abort with an "out of memory" error when I compile with extended debug information?

    The compiler instruments the source file with a lot of support code to provide things like arbitrary restarting/returning from activation frames. This will transform calls in tail-position into non-tail calls in all situations but the most trivial ones (do loops and named let). This will result in a much higher memory usage for allocating continuation frames. Try passing the -:hXXX option when executing the program, with a large value for XXX.


  24. Why do I get a warning when I define a global variable named match?

    Even when the match unit is not used, the macros from that package are visible in the compiler. The reason for this is that macros can not be accessed from library units (only when explicitly evaluated in running code). To speed up macro-expansion time, the compiler and the interpreter both already provide the compiled match-... macro definitions. Macros shadowed lexically are no problem, but global definitions of variables named identically to (global) macros are useless - the macro definition shadows the global variable.
    This problem can be solved in one of three ways:

    • Use a different name
    • Undefine the macro, like this:
      (eval-when (compile eval) (undefine-macro! 'match))

  25. How can I enable case sensitive reading/writing in user code?

    To enable the read procedure to read symbols and identifiers case sensitive, you can set the parameter case-sensitivity to #t.


  26. When I use callback functions (from Scheme to C and back to Scheme again), I get weird crashes.

    There are two reasons why code involving callbacks can crash out of know apparent reason. The first is that it is important to use foreign-callback-lambda/foreign-callback-lambda* for the C code that is to call back into Scheme. If this is not done than sooner or later the available stack space will be exhausted.
    The second reason is that if the C code uses a large amount of stack storage, or if Scheme-to-C-to-Scheme calls are nested deeply, then the available nursery space on the stack will run low. To avoid this it might be advisable to run the compiled code with a larger nursery setting, i.e. run the code with -:s... and a larger value than the default (for example -:s300k), or use the -nursery compiler option.
    Note that this can decrease runtime performance on some platforms.


  27. When I run csi inside an emacs buffer under Windows, nothing happens.

    Invoke csi with the -:c runtime option. Under Windows the interpreter thinks it is not running under control of a terminal and doesn't print the prompt and does not flush the output stream properly.


  28. How can I change match-error-control during compilation?

    Use eval-when, like this:

      (eval-when (compile)
        (match-error-control #:unspecified) )
      

  29. Why doesn't CHICKEN support the full numeric tower?

    There are a number of reasons for this:

    • I haven't found a decent library for multiprecision arithmetic, yet (the GNU multiprecision library is distributed under a different license than CHICKEN);
    • For most applications of Scheme fixnums (exact word-sized integers) and flonums (64-bit floating-point numbers) are more than sufficient;
    • Interfacing to C is simpler;
    • Dispatching of arithmetic operations is more efficient.

  30. How can I put toplevel definitions inside eval-when in combination with the highlevel macro system?

    eval-when expands into an internal special form, which effectively puts the contained body into a non-toplevel context with the syntax-case macro system. An ugly but working solution is to use set! instead of define.


  31. Compiling very large files under Windows with the Microsoft C compiler fails with a message indicating insufficient heap space.

    It seems that the Microsoft C compiler can only handle files up to a certain size, and it doesn't utilize virtual memory as well as the GNU C compiler, for example. Try closing running applications. If that fails, try to break up the Scheme code into several library units.


  32. Toplevel-continuations captured in interpreted code don't seem to work.

    Consider the following piece of code:

      (define k (call-with-current-continuation (lambda (k) k)))
      (k k)
    When compiled, this will loop endlessly. But when interpreted, (k k) will return to the read-eval-print loop! This happens because the continuation captured will eventually read the next toplevel expression from the standard-input (or an input-file if loading from a file). At the moment k was defined, the next expression was (k k). But when k is invoked, the next expression will be whatever follows after (k k). In other words, invoking a captured continuation will not rewind the file-position of the input source.
    A solution is to wrap the whole code into a (begin ...) expression, so all toplevel expressions will be loaded together.
  33. How can I specialize a generic function method to match instances of every class?

    Specializing a method on <object> doesn't work on primitive data objects like numbers, strings, etc. so for example

      (define-method (foo (x <my-class>)) ...)
      (define-method (foo (x <object>)) ...)
      (foo 123)
    will signal an error, because to applicable method can be found. To specialize a method for primitive objects, use <top>:
      (define-method (foo (x <top>)) ...)

  34. I use SWIG to create FFI wrappers, and my application seems to be caught in an infinite loop for no apparent reason.

    There is a bug in the SWIG code that generates the wrappers. Matthias Koeppe kindly fixed this (and another one), so you should use the newest CVS version of SWIG.


  35. Why does a chicken have both dark and white meats?

    Short answer is chickens don't fly long distance. As a result the muscles in the wings are designed for short bursts of activity and don't have as many of the proteins that facilitate oxygen distribution in those muscles. These proteins are coloured red and cause the dark colour of the meat. Ducks fly a lot hand have dark meat in their wings. Chickens do walk a lot so their leg and thighs are dark meat.

    (Thanks to Chris Double)