Chicken's mediocre performance is causing me problems, so I'm taking yet another language tour/spike.
Looking at Gerbil Scheme, as a faster Scheme and as a bonus it's closer to R7RS. It has far less library support than Chicken, but does have a FFI, and compiles to a native binary through C, so I really don't need too many libraries.
Instead of the #!key #!optional #!rest keywords used in Chicken and some others, Gerbil uses a literal template style for functions:
;; Chicken: (define (foo a #!key (b 'bar) #!optional (c "see?") #!rest rest)
;; Gerbil:
(def (foo a b: (b 'bar) (c "see?") . rest)
(print "a=" a " b=" b " c=" c " rest=" rest "\n")
)
(foo "Mark" 1 b: "bees!" 'what) ;; note out-of-order optional & keyword
;; a=Mark b=bees! c=1 rest=what
I like this style better, but I dislike the "def" keyword (which uses the enhanced lambda that does this) instead of "define" which uses "lambda%" (traditional lambda).
Gerbil uses []
for list construction, and {}
for method dispatch, so goodbye to the nicely distinguishable braces I was using in Chicken. The spec says implementations can do what they want with those symbols, but I wish they wouldn't. Ah, well. I'll add a shitload more whitespace and some comments and it'll be fine.
The struct/object system in Gerbil is pretty nice, they're slightly upgraded records, but there's an (@ obj field)
syntax instead of (MyClass-field obj)
, and (defmethod {mymethod MyClass} ...)
creates a polymorphic {mymethod obj args}
which finds the right method for any object, which is especially useful for inheritance.
I tried doing some VT100 graphics just to proof-of-concept, but it's doing something to the terminal, such that the same escape codes which work in Python don't clear the screen fully in Gerbil. After a short losing battle with stty and termcap, I give up on that and I'll jump right to writing a C FFI to SDL, because in 2019 that's easier than writing to a console.
Daily reminder that everything we have made since 1984 is overcomplicated junk. On an Atari 800, this took a few seconds to type, and you could start coding a nice UI instantly:
Alas, we live in a fallen world, so this is going to be trouble. Here's my Gerbil FFI template so far:
package: myffi
(import
:std/foreign
)
(export #t)
(begin-ffi
;; names of all Scheme wrappers to expose
(chello)
(c-declare #<<CDECLEND
#include <stdio.h>
int chello(void) {
printf("Hello this is C!\n");
return 1;
}
CDECLEND
)
(define-c-lambda chello () int "chello")
) ;; begin-ffi
; TODO for constants: (define FOO ((c-lambda () int "___result = FOO;")))
(define (main . args)
(chello)
)
Gerbil has a Scheme-based build system, but I'm a caveman so I make another build.zsh:
#!/bin/zsh
function usage {
echo "Usage: build.zsh MAIN.scm [LIBS] || -?"
exit 1
}
if [[ $# -eq 0 || "$1" == "-?" || "$1" == "--help" ]]; then
usage
fi
mkdir -p bin
main=`basename $1 .scm`
gxc -exe -static -O -o bin/$main "$@" || exit 1
echo "Built bin/$main"
Now:
% ./build.zsh myffi.scm
Built bin/myffi
% bin/myffi
Hello this is C!
Hooray! Unconditional success! Only took all afternoon and a half-pot of coffee!
Now I "merely" have to wrap all of SDL (well, just the parts I need) and get linking working and oh sweet merciless Cthulhu. And I still won't know how much this'll help my performance until I'm days into it. But, the first step is the hardest.