Why guile scheme is both beautiful and ugly

The high-level APIs in guile scheme are beautiful. Macro API is beautiful. Continuation API is okay.

On the other hand, 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. Uglier than java and javascript. I realized that these ugly APIs are probably low-level APIs that were designed for performance.

If guile scheme is going to become the foundation of a lisp machine, these low-level APIs can make sense for performance.

Perhaps, one can even write a kernel in scheme with low-level APIs?

1 Like

I think low-level APIs don’t have to be ugly. They can probably be re-designed to look beautiful, but doing so probably breaks backward compatibility.

If someone was designing a lisp language from scratch for low-level and high-level programming, it can look beautiful for both low-level and high-level APIs.

Is this going to be a herd joke?

This is not a joke. It is simply the impression I got while I was studying guile scheme.

If I was joking, I would make it obvious.

While scheme language standard is fine for programming education, I think scheme has been overhyped a bit. I’m going to use guile scheme, but I don’t love it.

Fortunately, scheme isn’t just a language, it’s a meta-language. The point is that you can design any syntax you feel appropriate. This comes with many of the problems of DSLs, which only become apparent later (if your DSL misses the mark, for example)

I don’t really speak from experience though. So idk.

I like the ports interface. It feels very unixy.

Here’s a speedrun through more advanced integration

A particular pain point of many scheme APIs is place-oriented programming that was criticized by rich hickey.

Most Port functions use positional arguments instead of keyword arguments. They also force you to deal with offset and whence. Offset and whence come from C. Many guile scheme APIs expose you to low-level C abstractions. Dealing with offset manually is painful.

Python has been criticized for many reasons, but its APIs at least give you keyword arguments instead of positional arguments. Python APIs also don’t expose you to things like offset.

I don’t see keyword arguments in low-level programming. That requires management of symbols and usually recursion. You can’t have recursion at the lowest levels.

But again, I really don’t know. You sound smarter than me. It’s really not that unusual around here (which is a good thing)

I guess if I avoid low-level APIs, guile scheme may not irritate me.

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.