返回资讯中心
外部精选
软件工程
#开发者实时流

My Perfect Scheme

Keep up the good work, my fellow cleanweb person!

Hacker News 最新Linux-Fan2026/10/22 · 23 天前17 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

Oh, I see you disabled JavaScript. Keep up the good work, my fellow cleanweb person!

Notice that there might be trace amounts of JS, used for:

JS is not required for use of the website though, it’s only enhancing the existing functionality.

So I have recently picked up Scheme. I like it! While it’s not as featureful as Common Lisp, it wins by being really sane and simple. In fact, a basic implementation can take as little as 6KLoC of C!

But there are some things that I want in my perfect Scheme. Sometimes just as a development of already existing themes. Sometimes some really unconventional things. My perfect Scheme might not rhyme with yours, but let’s agree to disagree. My perfect Scheme is:

Common Lisp is burdened with some sloppy historical accidents. Say, the concept of a symbol designator (symbol, string, or character!) or pathname designator (pathname, stream, or string, what?!) It brings some power but is quite unpretty.

Scheme, on the other hand, is quite strict and exact. Almost all standard procedures are well-typed and domain-restricted. I like that!

However, it’s not enough sometimes. So I bit the bullet and started my own SRFI series. Regarding type checking and enforcement. SRFI 253—the principal one of these—is now merged in most implementations I care about. I can write perfectly well-typed code and be certain that it is correct and well-documented—at least type-wise.

Sequels to 253, SRFI 273 and SRFI 283—they add some more niceties, but are not really required for proper typed Scheme.

I really enjoy Scheme’s axiomatic nature. A really small set of special forms (10 in Chibi, 17 in Teeny Scheme!) It all can fit in my head. And the implementation can fit in my head too! Teeny has 6K lines of code, all of Skint is mere 11K lines, Chibi is 30K lines. Guile is… we don’t talk about Guile in this regard. So Scheme is small and easy to implement. And that’s beautiful, just perfect.

Parentheses. They are fun… unless you use ed(1). No—not vi(1). ed(1). It’s line-based and unaware of ASTs. So I have to come up with my own syntax for Lisp to edit it properly.

So my perfect Scheme is this: line based or at least close to it. I made a macro doing some heavy lifting for that:

(define-syntax %%block (syntax-rules (:) ((%%block (form ...) : . rest) (form ... (%%block () . rest))) ((%%block (form ...) something . rest) (%%block (form ... something) . rest)) ((%%block (form ...)) (form ...))))

;; TODO: Support return keyword (define-syntax %block (syntax-rules (_ !) ;; Terminators with bindings: return last binding ((%block (var form) (vars ...)) (let ((var form)) var)) ((%block (var forms ...) (vars ...)) (let ((var (%%block () forms ...))) var)) ((%block (form) (vars ...)) form) ((%block (forms ...) (vars ...)) (%%block () forms ...)) ;; Terminating clause recursion: remove trailing exclamation ((%block (forms ...) (vars ...) !) (%block (forms ...) (vars ...))) ;; Ignore exclamation at the start of the code or when duplicated ((%block () (vars ...) ! . rest) (%block () (vars ...) . rest)) ;; On every exclamation with binding, wrap into a let ;; First multiple values, then regular ones ((%block ((mv-var mv-vars ...) form) (vars ...) ! . rest) (let*-values (((mv-var mv-vars ...) form) ((var) mv-var)) (%block () (vars ... var) . rest))) ((%block ((mv-var mv-vars ...) forms ...) (vars ...) ! . rest) (let*-values (((mv-var mv-vars ...) (%%block () forms ...)) ((var) mv-var)) (%block () (vars ... var) . rest))) ((%block (var form) (vars ...) ! . rest) (let ((var form)) (%block () (vars ... var) . rest))) ((%block (var forms ...) (vars ...) ! . rest) (let ((var (%%block () forms ...))) (%block () (vars ... var) . rest))) ;; Insert exclamation for let-s automatically ((%block (var forms ...) (vars ...) another-var rest) (%block (var forms ...) (vars ...) ! another-var rest)) ;; On every exclamation otherwise, generate a var (useful for _ below) ((%block (form) (vars ...) ! . rest) (let ((var form)) (%block () (vars ... var) . rest))) ((%block (forms ...) (vars ...) ! . rest) (let ((var (%%block () forms ...))) (%block () (vars ... var) . rest))) ;; On every underscore, refer to the innermost var ((%block (forms ...) (vars ... var) _ . rest) (%block (forms ... var) (vars ... var) . rest)) ;; Otherwise, just collect the form ((%block (forms ...) (vars ...) something . rest) (%block (forms ... something) (vars ...) . rest))))

;; Special minimal-parens syntax ;; Provided niceties: ;; Bindings with var form ... ;; Multiple value bindings with (var ...) form ... ;; Anonymous / threading / chaining / pipelining vars via _ (block + 1 2 ! * _ 3) ;; Automatic parenthetication of forms until ! mark: + 1 2 3 ! -> (+ 1 2 3) ;; Wisp inline colons: display : + 1 : * 1 -> (display (+ 1 (* 1))) ;; Omission of ! when the next form is a binding (define-syntax block (syntax-rules () ((_ . forms) (%block () () . forms))))

block macro with line-based syntax

But it’s not enough. So I’m currently in the process of brainstorming a new syntax for Scheme. Something like Wisp, but not quite.

Oh, and square brackets. These come from R6RS and they are beautiful. And helpful in line-based editing, because they act as diverse anchors for regex!

I want to edit my Scheme in the most Spartan circumstances. Including ed(1)—that’d be perfect.

Common Lisp is stable. Which is good! But I sometimes wish there was some way to bring new features into the language. And make community standards. Yet, the only such attempt, CDRs… it has died out.

Unlike in Scheme—where the process of SRFI (Scheme Requests for Implementation) allows to propose a new language feature and actually see it implemented!

So my perfect Scheme is this: community-driven and living-breathing.

My go-to list of SRFIs for practical programming is:

This is a sizeable list, true. But most of these are de-facto standards implemented by most major (and minor) Scheme implementations. So it’s all safe!

Scheme implementations have REPLs—almost always. While it’s quite saddening and crude at times—it’s still better than most languages. And it’s often the case that things can be introspected from the REPL too! (If they can not in your implementation—I’m coming for you with my new introspection SRFI!)

Trying to reverse-engineer an implementation is a pleasure in itself. While I still have to reach for sources of Chibi or Skint, exploring the REPL is a fun way to test and break things. Recommended as perfect exercise in playful programming!

So, my perfect Scheme is the one that can be strict. That stays simple and small. That’s easy to read, edit, and reason about. Community-driven and changeable by consensus. And breakable from inside the implementation too!

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →