@cwebber Christine it’s 4am, why are you posting
Notices by Daphne Preston-Kendal (dpk@chaos.social)
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Wednesday, 12-Feb-2025 18:45:30 JST Daphne Preston-Kendal
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Monday, 10-Feb-2025 05:51:30 JST Daphne Preston-Kendal
It’s done. https://codeberg.org/dpk/extensible-match
SRFI submission any day now.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Friday, 07-Feb-2025 19:28:26 JST Daphne Preston-Kendal
@cwebber I know someone who wrote this really good and long post about the differences, hang on let me find it
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:55 JST Daphne Preston-Kendal
Why I hate record types: a belated answer to @kitten_tech
Let’s first consider the opening paragraph of the Scheme reports. I truly believe the statement made by the first sentence: *all* programming languages should be designed by removing restrictions, rather than by feature piling.
I don’t think Scheme is unique in succeeding at following this philosophy, but I think it shares with only a few other languages the distinction of having achieved this goal.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:54 JST Daphne Preston-Kendal
It’s at once an honour and incredibly daunting to be responsible for writing the report that sits under these words.
Granted, Scheme hasn’t always succeeded here: undelimited continuations have stuck around far, far too long for something which composes so poorly *with itself.*
When we consider new features for R7-large, this maxim comes up often. (Like any such maxim, it’s vague enough that it can end up being wielded by multiple sides of a debate, each accusing the others of betraying it.)
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:53 JST Daphne Preston-Kendal
I have a fun little list on the R7RS wiki of features that seem nice to have in a record system. Some of them are from R6RS, some from SRFIs, some from Scheme implementations, some from other Lisps.
The reason I made this page is *not* because I want to put them all in R7-large, nor even that I want to consider them all for R7-large, but to show how bloated record systems can become; to show why record types are a uniquely awful part of evolving a language like Scheme.
https://codeberg.org/scheme/r7rs/wiki/Record-system-features
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:52 JST Daphne Preston-Kendal
But it’s not only the sheer number of features, but their composition with one another. Ideally, if we were to follow the Prime Clingerism, we would have a nice, small, composable core system where people could add the features they want – much like they can use continuations, closures, and hygienic macros to fill in the gaps adding features to other parts of the Scheme language.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:51 JST Daphne Preston-Kendal
Let’s take one example on that page: type-checked fields. Well, surely you can implement *that* in terms of a simple record interface! SRFI 253 does it (define-record-type-checked)! Just make sure the constructor checks the type of all the fields when the record is created, and any mutation procedures check that the new value for a field also satisfies the type check. Easy, no?
Well …
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:50 JST Daphne Preston-Kendal
If you have what R6RS and SRFI 99 call ‘procedural’ and ‘inspection‘ layers, someone can go in through the back door and create a record constructor procedure or a field mutator procedure which doesn’t do the type check. So these two quite simple features – one very commonly provided by implementations, the other very commonly wished for by users – actually need to be baked into the core of the record type system, so new constructors and mutators also apply the type check.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:48 JST Daphne Preston-Kendal
‘Oops, okay’, you think. ‘Well, I know I‘m never going to expose the record-type descriptor for this type, so I’ll just declare it (opaque #t). Then nobody outside my module can access the procedural record features and break the type check.’
Hang on, though – record type opacity is another feature! Have you thought about what you’re making harder by including that feature in your record type system?
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:47 JST Daphne Preston-Kendal
In the case that you truly control the record type and know it either won’t be subtyped, or you’ll be the one subtyping it, opacity is fine, and does indeed stop composition-breaking shenanigans like this.
But when we add features, we don’t just add them for the cases where they’re useful: we also have to consider the cases where they’re not useful.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:46 JST Daphne Preston-Kendal
If you’ve been following me recently, you know where this is going: no matter what you do, even if you know the record structure of a parent type, you can’t access the record structure of an opaque subtype in order to, say, make a copy of the record. So record opacity shuts out the ability for users to implement, for example, functional field updaters themselves – no matter how useful it is in the cases where opacity actually helps.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:45 JST Daphne Preston-Kendal
So it goes on and on. Record type system features are just really, bizarrely bad for composing poorly with one another: worse than anything else in PL design that I’ve experienced.
What’s a Scheme report working group to do?
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:44 JST Daphne Preston-Kendal
R6RS chose a nice set of features – single inheritance plus the ability to disable it, opacity, nongenerativity, procedural inspection and type creation – and gave them a very nice syntactic interface and a not-much-worse procedural interface. Its design is easy for an implementation to provide with good efficiency, and it’s formally absolutely correct, even preserving encapsulation under subtyping in a nice way.
Unfortunately, attempts to add other features to it invariably run into problems.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:43 JST Daphne Preston-Kendal
(At least if you’re not the maintainer of an entire Scheme implementation – if you are, you can do what you want, by just baking all these things into the core of your record system. But that’s the essence of feature-piling, and is not The Scheme Way.)
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:42 JST Daphne Preston-Kendal
R7RS small went the other way, cleaning up SRFI 9 (although not quite enough), providing nothing but flat records with a fixed field structure, with none of the R6RS features – no procedural layer, no inspection, not even subtyping support!
Despite this, the R7RS small way has a surprising amount to recommend it. In particular, because it provides basically nothing apart from a disjoint type predicate and some slots, you *can* actually implement a more featureful type system on top of it.
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:41 JST Daphne Preston-Kendal
Implementing something like subtyping does have a performance cost, alas, compared to if subtyping were natively supported: you have to store the field values in some kind of vector pointed at by one of the ‘real’ fields of a subtypeable record type, which is an extra indirection, extra allocation, extra memory usage, and a bounds check you can strictly do without. And while you’re at it, you *probably* stomp the compiler’s ability to optimize with compile-time record instantiation, sigh
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:40 JST Daphne Preston-Kendal
Nonetheless, my personal preference remains that R7-large add should nearly nothing over its smaller brother, for this reason. Essentially, what this would give you is a ‘pick two of three’ scenario, where the three are maximum runtime efficiency/support for subtyping/portability between implementations. (Implementations could still provide their own native subtyping, of course.)
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:39 JST Daphne Preston-Kendal
Unfortunately (for me), Scheme users and the others in the WG really want native support for subtyping. SRFI 256 is my attempt to find an inoffensive means of doing this as a minimal extension, but I think it fails: I’ll probably withdraw it rather than take it through to finalization.
Plus, even once we have subtyping, our commitment to transparent R6RS interoperability *probably* means it has to be the R6RS subtyping system, with its baked-in sealedness and opacity support. Grumble grumble
-
Embed this notice
Daphne Preston-Kendal (dpk@chaos.social)'s status on Thursday, 09-Jan-2025 06:29:37 JST Daphne Preston-Kendal
There is one other option I’ve considered: providing a define-[something? not sure what to call it]-type which gives you a disjoint type whose field size is determined at instance construction time, not at type definition time.
This would let you implement subtyping yourself with near-native efficiency – only two problems:
1. you’d still need the bounds check although it’ll be pointless in 99.99% of cases
2. you still stomp the ability to do compile-time instantiation