I couldn’t stop feeling that many guile scheme APIs like port APIs are ugly. While I read guile scheme manual, I thought I was studying ugly C APIs.
Unix has a different standard of beauty than Lisp. That’s the gist of the famous “Worse is better” essay ( Worse is better - Wikipedia ). Guile (and GNU in general) are trying to bring the two together.
I think ports is unix-beautiful. A port is basically a Unix file descriptor with a little metadata to make it fit better in scheme. That’s why it’s so low-level. It approximates the C interface very closely. But that isn’t relevant for the common use cases. Usually you can get by with with-input-from-file and friends.
There’s also ugly bits in Guile because of backwards compatibility. The codebase is older than I am, and I found some grey hairs in my beard recently. For example, it’s kind of ugly that there’s parameters AND fluids, new code should just use parameters, but parameters are built using fluids. Taking away fluids would just break old code for no reason. The rest of us can just ignore them. So they stay.
Perhaps, one can even write a kernel in scheme with low-level APIs?
That is basically what the Lisp machines did. There’s been some experiments to do since then.
But the approach taken by GNU and Guile to approximate the lisp machine experience in userspace with a Unix-style kernel. Stallman explains why he chose that somewhere, but I can’t find it. IIRC he would have preferred to have a Lisp machine, but the hardware that would make that practical was not accessible, and Unix already had a strong code-sharing culture.
Today’s hardware could handle a lisp machine. Stufflike multiple cores, pipelining and instruction-level parallelism, and branch prediction, can kind-of automatically do the stuff the specialized lisp machine hardware did, and today’s compilers like SBCL (or your browser’s javascript engine) can do great type inference, removing the dynamism anyway.
But there’s very compelling reasons to write kernels in a language more like C than like lisp. The system, at some point, needs to see the low level stuff (memory is bytes, no GC, etc). You could do a mix of assembly and scheme. But some of that assembly should probably be C for portability and readability. And some of that scheme should probably be C for efficiency. And then before you know it, you have a kernel in C.