Thanks for your questions.
Is there a way to access args higher than the third from C?
Yes, definitely. You could define macros as follows:
#define fourth(x) (car(cdr(cdr(cdr(x))))
#define fifth(x) (car(cdr(cdr(cdr(cdr(x))))
#define sixth(x) (car(cdr(cdr(cdr(cdr(cdr(x))))
and so on, or you could just write the sequence of car() and cdr() inline.
I guess (void) env, which I seem to see at the beginning of every function, discards the environment argument. Same thing with (void) args when no args are provided. Are these necessary, do they optimize something or is this "just in case”?
They are included in functions that don’t use one or other of those parameters, and simply avoid getting a warning from the compiler that you haven’t used the parameter in the function.
Do functions need to have a return value? Would it make sense to document t as the default return value?
All Lisp functions return a value. If the function is used for its side effect, such as print, the returned value is usually chosen to be something useful. For example, with print the returned value is the value of the argument, so you can insert (print …) around an expression for debugging without affecting the operation of your program. Usually if there’s no sensible value to return a function will return nil.
Regarding error handling: the types you can check for are lisp types, and type checks are already included in the conversion functions, right? At least I already get type errors if I try to call e.g. (red “10”).
Yes, you’re right, most of the conversion functions check for the correct type.