One of the issues R7RS-large ran into was in how to handle datatypes, such as collection types. Initially, Lisp-style languages had linked lists as the primary data type, but vectors were early added. So you had a "map" function (that worked on linked lists) and "vector-map" (that worked on vectors). Similarly, "length" vs "vector-length". What happens if you want to add more collection types - for example "bytevector"? Do you add "bytevector-map" and "bytevector-length"? Or do you generalize "map" and "length" so they also work on bytevectors (and also vectors)? The former is most compatible with Scheme history and tradition. The latter is IMO preferable going forward as new collection types are added - but it needs some way to talk about frameworks of types that are all sequences (linear collections). I.e. you need at least the concept of inheritance or "interfaces" as used in various languages, including Java and TypeScript.
Many languages in the Lisp/Scheme family have some kind of inheritance or interfaces, including Common Lisp, Dylan, Racket, Kawa, and many more. However, this hasn't been standardized for Scheme. R7RS-small has a mechanism to define new data types (records), but they were limited in functionality to C-like structs.
I (and others in the community) felt (and feel) that it is premature and unwise to add a bunch of new libraries for new data types until or unless we have some kind of framework for inheritance or interfaces. Others in the community (including the former chair) felt this was unnecessary and perhaps inappropriate for a language with the goals and history of Scheme.
There is no clear right answer or consensus for how to go forward, which is part of what caused things to bog down.
I learned Scheme from George Springer in the '90s.
I just want a language with S-expression syntax and a proper numeric tower (like Scheme), with modern HAMT-based immutable collection types (like Clojure), and a native JIT so I don't have to mess with Java or JavaScript.
Does Racket fit the bill now?[1][2] Coming from Indiana, I was happy when they announced the move to Chez.
I was heartened by a post here yesterday about implementing a numeric tower on WASM.[3]
The lack of “interface” was one of the main reasons I never ended up using scheme to build production systems.
I loved playing with scheme in my university days 20 years ago, and loved how one can make up any fancy programming concept using call/cc and dynamic-wind. Except if you want to have multiple implementation of some protocol or collection. Hand roll your own object model or dynamic dispatch mechanism? Hard to make it scalable, composable and compatible with other projects.
> Initially, Lisp-style languages had linked lists as the primary data type, but vectors were early added
The Achilles' heel of every Lisp is dealing with any data structure that isn't a linked list. I'm talking aref, setf+aref, vector-ref, vector-set!, char, etc. It's all just a huge pain when compared to any other language (Python, Ruby, JavaScript, Java) that has syntax for arrays, hash maps, strings. Solving the generalized "map" or "length" is really only solving one side of the problem. The other side can't be solved because the language is s-exps and "everything is a function." It would fundamentally cease to be Scheme at that point.
I haven't used clojure since forever but I don't think this was particularly problematic. They even had nice literal syntax for different collection types and fairly powerful generalized collection abstractions, way better than the languages you mentioned IMO.
Also Clojure decomplects inheritance and interfaces.
It gives you protocols to define a set of related methods, without any restriction on the underlying data structure.
It gives you defrecord & deftype to define composite associative data structures, which the fields in an object are.
Then you can combine these in various ways, giving you all sorts of polymorphic code.
Clojure even has multimethods for cases, when a single operation should happen different ways, depending on some aspect of an arbitrary data structure.
Someone defines a transformation function from arbitrary data to some other data to dispatch behavior over, then anyone can add define behavior for new dispatch values.
> At the same time, the community identified that there were two groups with slightly different concerns about what R7RS Large would look like: some were concerned that the small language itself didn’t impose strict enough semantics for a safe high-level programming language,§
> § Indeed, the small language doesn’t even enforce bounds checking, even though almost all implementations do it. Such are the sacrifices for a language which might be expected to work on a tiny microcontroller. ↑
Okay but nobody in their right mind is writing Scheme for microcontrollers. I don't mean that nobody is doing that, but that the people who are are fringe weirdos whose desires should not be allowed to disrupt the evolution of the language for the rest of its users!
For production purposes, I would normally try not to run GC on a traditional microcontroller, whether it's Scheme, Python, JVM, or anything else.
But it's maybe worth noting that using Scheme metalanguage abilities to generate non-GC target code for a microcontroller could be very sensible.
For example, you have nontrivial control behavior, and there's a short lead time on the controller or device. So, you could define a macro/minilanguage to support modeling the behavior in terms of Harel Statecharts, and initially (for very quick development) have that expand to executable normal Scheme code, for development/testing/simulation. Then, once you've gotten the behavior you want, you tweak the macro/minilanguage a little, to also expand to non-GC target assembly, for the microcontroller dev board or device that you ordered when you started.
(Racket syntax extension and submodules are especially nice for doing something like this, but you can also do it with more basic Scheme.)
Cisco is a major backer and implementer of its own scheme implementation. Cisco only ever works on embedded stuff so it makes me think that somebody is doing it somewhere.
Please correct me if I'm wrong here, I'm not trying to insult anyone or start a war. But this is a scheme working group, right? Is scheme really that important today to so many people? I haven't touched lisp since I stopped using EMACS about 10 years ago. I haven't heard about anything interesting involving lisp (except I've heard of clojure) since even before then.
Nyxt browser is written in Common Lisp and is going from strength to strength. Think of an attempt to provide the power of Emacs, but built on Common Lisp, and obviously with the graphics and video support browsers have.
GNU Guix has 24,207 packages available and is written in Guile Scheme. The name comes from "Guile Nix", as it's based on Nix, so functional packaging, roll-backs, reproducible builds, etc. That might not be your thing, but it's definitely a very active and "successful" operating system.
What a really great write up on the situation (from a certain point of view). Nice. I still want to play with either Chicken Scheme or Racket some day.
Do you sneak it in as the language for the implementation or as a DSL or scripting language? It's an interesting little language implementation, which I enjoy playing around with, but I never found any project where the team as a whole wasn't better of with something like Python.
I have no context but I'm going to tell you what I think anyway.
I think this is Conways law in action: See W3C, Whatwg and C++ standards committees. The work of specification difficult and the work is complicated and hard. I've never written a standard but I imagine it's very difficult.
I've thought how we can use Conway's law directly to engineer technical and social systems that are robust and meet people's needs.
One of my ideas is that for a group of different individuals and differing interests and priorities to collaborate, you need to recursively define collaborations and treat them as portfolios where the traversal of those collaborations/interactions is a standard, like Git.
What do I mean?
I want X, so I announce I'm doing X and how I'm going to do it and I do X. Then I publish X. Then someone says I want that, can I import it. X isn't to their tastes, so they announce they're going to change it to do Y. Or they negotiate with the author of X to change it to do Y. This is a collaboration between those two entities, it's a portfolio of collaborations. That's a standard right there.
The doing X is the important part.
In other words, the collaboration between parties is reified communication and agreement that is actually imported and used.
In my mind the complication in this is: given you did X, and is using X to do A, when Y is proposed and is not compatible with how X works to do A then you have the cost of migration/adaptation of whatever is using X to converge to Y while still being able to do A, otherwise you end up in the same issue with competing standards.
This can be much, much worse if it involves anything that is not purely software, you can't adapt older hardware using a standard (let's call it notUSB) to iterate over an improved notUSB-with-appendages, and if notUSB-with-appendages required modifications incompatible with notUSB you just created 2 standards that don't solve a general problem.
Standards is something that a lot of people have thought about over centuries, any improvement on the process has probably already been considered by many smart folks so I don't think there's a simple coordination solution to it.
I think you are right. All the most successful standards have described existing implementations. They didn't describe the universes they hoped it would be in the future, they described the situation on the ground now, perhaps with some compromises in place to try to get everybody on the same page. Look at Common Lisp, SQL, POSIX.
There was amusing humor in this writing with allusion to Douglas Adams & Kurt Vonnegut (& maybe others I missed). This seems especially apt for a young person with ambitions to chair a committee for standardizing such an old language.
"So it goes" can't be called a Vonnegut reference unless it's surrounded by other Vonnegut references, because it's a perfectly ordinary phrase in common use outside Vonnegut.
Think of it this way: "So long, and thanks for all the fish" is a Douglas Adams reference, but mentioning a dolphin isn't.
An "allusion" is an indirect reference/pun. The degree of indirection is clearly subjective. The phrasing great grandparent recognized from Adams (at least one other person) is actually a negation of the famous line from Restaurant At The End of the universe. (EDIT: which, for the record, is "The story so far: In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move.")
I do agree "So it goes" is more of a stretch here especially since dpk seems to live in Germany and it's a common translation of "So geht's" which may be more common in Germany than "So it goes" in primarily English speaking countries. I.e., how "perfectly ordinary" may be reader-dependent. I think it's a stretch supported by the Adams adaptation two paragraphs earlier, though. In primarily English speaking countries it is "signature enough" that they use it for the Vonnegut Library: https://www.vonnegutlibrary.org/so-it-goes/ , but sure it comes up a lot more than "thanks for all the fish". I think these are disputes of degree more than kind.
TL;DR - I stand by my guess, but you'd have to ask dpk to be sure.
EDIT: and Vonnegut's parents were German - so, this may also just be V being an "ambassador" making the more "perfectly ordinary" in one domain more "signature" in another domain. It happens.
Well, first of all Racket more closely resembles r6rs. The main large r6rs implementations (guile and chez) both have proper threads (guile even has a parallel CML, which is probably my favourite way to do parallelism of all time) and a startup time measured in single digits (ms) compared to hundreds for racket and something like 80 for racket/base on my slow computer.
I am an outsider in this conversation (in the last 40 years 95% of my Lisp work used Common Lisp, 5% some Scheme dialect). I do have a recent frustration experience that is relevant though: last year I was thinking of making a serious effort to use Chez Scheme more and I hit a minor roadblock trying to find a library to UUENCODE text. Maybe I was just having a bad morning, but it was frustrating. The Racket language had a ready to use library but I really wanted to use Chez.
This interaction illustrates my thought on the wisdom of supporting two language 'profiles'.
It seems like the 'small' profile matches what I expect from Scheme's brand: useful minimalism.
The 'large' profile seems off-brand. I get why it would be useful to have a Python-like experience (where 80% of problems can be solved with the stdlib), but doesn't that describe Racket?
I'm not suggesting that a batteries included standard Scheme would be a bad thing. It's just that it pretty much already exists (Racket started as a Scheme).
Lacking libraries can be quite a source of frustration, but would it have been so difficult to implement that functionality? In my experience, looking for a library and making it work often takes more time than to implement it.
Ahh the footnote on the forecasting based on the first and second Vatican Counsels forces me to offer additionally as a corollary the drafting of the Westminster Confession of Faith [0]
It was commissioned by the Parliament of England to settle theological disagreements between the Church of England and the Church of Scotland ( the progenitors of the Anglican and Presbyterian denominations respectively ) which were a major component of the English Civil War.
super short version - the Presbyterians wrote most of what came of the documents and ratified it completely, the Church of England red-lined it heavily and they never adopted it in full.
So I know very little about Scheme, other than it's fun to play around with.
Wouldn't there be some middle ground where you have a tiny language and a unifying library/plugin system that would allow libraries to work across implementations (given some API/restrictions). So much like we have C++ and Boost or C and various C standard libraries, it would be possible to have any number of small Scheme implementation that could, if the developers wanted to, pull in a larger standard library, of which there could be many. Each Scheme implementation could then opt to develop and distribute a standard library with their compiler or simply refer to any number of third party standard libraries.
Highly competent people may or may not be rare depending on exactly where you divide the line, but competent people in tech that are also competent to this kind of community wrangling are definitely rare, and people sharing both those competencies, with the tech competency in the right domain, who are interested in a particular community-wrangling job, and who can be trusted in it (which is distinct from either kind of competence) are often extremely rare.
Highly compentent people are rare. And those usually are very busy, especially if at the same time there are layoffs. And if you are compentent and have a plate full of exciting things to do in front of you, it is difficult to find the time for such a committee job, however important that on the long term might be.
I have an idea for maintaining backwards compatibility in programming languages.
Why not support old features of the language in the new version of the compiler/interpreter? This seems to be particularly feasible in Lisp/Scheme family of languages due to the fact that ASTs are edited directly in source code.
For example, each library declares its target version of the compiler, then newer compiler first translates the library to new syntax, then integrates it with other dependencies.
So let's assume there was wide spread consensus that it's a terrible idea and it should work like every other programming language. There is really no obvious way to simply "convert to the new syntax"? And trying to have "go with nil interfaces" library interact with "go with normal type system" would be it's own can of worms.
Like in this case, the only realistic path I could see would be you deprecate it, then you start warning, and then in like 10 years or something you fully remove it?
Good thought. Racket does this and more, for a long time.
For the "more": you can do it not only for other dialects of the same language, but also for very different languages. And often to make them interoperable between languages.
(For one simple example, long ago I made a fairly simple `#lang sicp` that translates the old dialect of MIT Scheme used by the SICP book, to Racket, using some simple syntax transformation and the module system.)
It still breaks compatibility for the library maintainers, and the results of automatic translation often aren’t pretty (as a basis for further development). It’s also difficult to test or prove that the automatic translation is 100% correct on arbitrary code. Furthermore, as a sibling comment notes, some language changes are made virtually impossible, even with source code translation, if backwards compatibility needs to be maintained.
Sometimes there is really no substitute for getting things right from the start.
But Scheme languages are particularly simple in terms of their syntax and logic to the point that it is often said people write more scheme compilers than scheme libraries.
I thought it would be possible to deterministically translate R^(n-1)RS to R^(n)RS
> I hope we could at least find some source of authority to give some kind of future specification its seal of approval.
> The situation reminds me somewhat of the First Council of the Vatican, which was suspended in 1870 after the unifiers of Italy invaded Rome, but was still technically in session until it was formally closed in 1960 in preparation for a new, Second Council of the Vatican.
Though the (presumably Protestant) author is understandably reluctant to come out and say it directly, it is clear that she knows that the ultimate guidance on Scheme language spec comes from its primary industrial user, the God of Abraham[0]. As such it's only natural the authority for approving R^(+ n 1)RS be vested in the Holy See with the pontificate responsible for the procedural and spiritual aspects.
It's possibly an unpopular take, but it's not completely clear to me what community-driven programming languages gain from a standards process anyway. (At least if the intention is to really standardize the complete thing, bells and whistles included, not just some tiny core, as R5RS did.)
If you have multiple adversarial commercial implementers for something, a standard makes perfect sense. It's still a lot of work, but necessary because otherwise, you wouldn't get things to interoperate at all. That's true for things like HTTP, or Bluetooth, or JavaScript, or C++.
But for a community project, having one blessed implementation and iterating quickly has proven to be the most successful model, in my opinion. I can point towards Python, Rust, even PHP for examples. Even Haskell, which started out with a written specification, has at this point de facto been forked into the much richer language called "whatever GHC understands".
Scheme isn't "a community project". It is an universe of many implementations. Having an agreed standard for this is very valuable. It gives a reference, which everyone will want to follow for interoperability. But by creating the standard, things are discussed, so flaws might be discovered and avoided and of course, it gives a clear definition.
In practice, Scheme isn't interoperable - you pick a Scheme implementation and pretty much stick with it. Different schemes implement different subsets of SFRIs, and many bring their own non-standard batteries to the table.
I think having r7rs large would eventually make the language more interoperable.
> But for a community project, having one blessed implementation and iterating quickly has proven to be the most successful model, in my opinion.
That's not how Scheme works, and I think it's important for both Scheme and PL research/design groups in general that they move the way they do.
Each Scheme implementation is in fact its own language, with its own goals, focus, community, etc. E.g. Racket has very different goals (like PL research, education) from Stalin (aggressive optimization), Guile (ease of embedding), or Chicken ("real world" usage). Both the RnRS and SRFI processes are more like guidelines for the implementations to follow, to enable at least some of the code (usually libraries) to be portable.
When you're writing your Scheme program, you're usually not targeting "R7RS" or "R5RS + SRFI-1" or whatnot; you're writing and running your program using Chicken, it's a Chicken program much more often than it is a R5RS program. If you later decide to port it to Racket, you will usually have much less porting work to do, but you'll still need to figure out how to do many of the mundane things omitted by the standard, like OS threads or FFI.
It's much more useful to think of Scheme as a loosely-tied family of languages, pretty much the same way you can think of UNIX-like OS's. Your average UNIX will surely give you a /bin/sh and a printf and a malloc, but the really interesting stuff (like kqueue, io_uring, DTrace, etc) is why you choose to stick with one over the other, and what motivates different groups to experiment / innovate / contribute.
How much of the problem is academianic? Scheme has been taught in renowned universities, but may be in decline thereabouts. How to get new blood into the pipeline without universities in the movement? How to get universities to teach the thing without intro to programming textbooks with examples that just work everywhere with reckless disregard of the local dialect and local implementation? Fortran and COBOL are about twice as old as Scheme and nowhere near as Balkanized and sectarian.
> To me, the common standard is what is really interesting.
It's exactly what I meant ;) Scheme is different things to different people, I've shared my PoV and (to me) it doesn't seem like it disagrees with yours.
> To move beyond R5RS, a Steering Committee was formed in 2004 which appointed editors to specify an R6RS. [...] A new Steering Committee was elected by those interested in Scheme in 2009
> All of the members of the Steering Committee have become somewhat less active in the Scheme community in recent years. Though we’ve seen occasional input from individual members, the Steering Committee as a body hasn’t taken any action since 2013 when the small language report was ratified. When the changes made in 2022 were being discussed, we got no guidance from the Committee at all. In short, we’re not sure the Steering Committee exists as a single, functioning entity any more.
How significant is this? Is there an actual process for forming a steering committee? From where does it derive its authority? If the current one is defunct, can they just form yet another one?
While some of the things said there cannot be assessed by an outsider, the last paragraph that starts with:
"Scheme has an outsized importance in the world of programming languages compared to its actual use in industry, because it has so often pioneered features that later broke into the mainstream.".
is doubtless true.
Scheme belongs to a list of at least two dozen historical programming languages that have introduced very important innovations and anyone who has the ambition of creating or improving any programming language should be familiar with all of them.
I consider sad the fact that all more recent attempts of introducing new programming languages appear to have been made by people who happened to have some brilliant idea about how to improve some feature of programming languages, but otherwise they seem to have little knowledge about past programming languages, so they combine their new good feature with various bad features from a few languages that happen to be popular now, due to various historic accidents, even if already many decades ago better alternatives were known.
Because of this continuous reinventing of the wheel, all the available programming languages include various combinations of good features with bad features, so none is completely satisfactory.
Does there exist a catalog of these pioneering or best practice features, and bad features that should not be replicated? It might be worthwhile to have a tome of such for future language designers to learn from.
> Does there exist a catalog of these pioneering or best practice features, and bad features that should not be replicated? It might be worthwhile to have a tome of such for future language designers to learn from.
A "programming language features" wiki cataloging such features (and their pros/cons) would be amazing.
In C, the escape character for literal strings, as interpreted by the C compiler, is the backslash. ("\")
Also in C, the escape character for format strings, as interpreted by the printf function, is the percentage sign. ("%")
This is the correct way to do things, for the very obvious reason that neither the compiler nor printf can get confused by the input the programmer provides. Directions to the compiler look nothing like directions to printf.
But for some reason, this knowledge of how to define formats was completely forgotten, and so regular expression parsers were all defined to use a backslash as their escape character. There is no earthly reason you would want to do this. It causes many, many problems, and has no benefits, to use the same escape character for the compiler/interpreter and for the regex engine. But we're doing it anyway.
I am not aware of any such catalog, so I had to compile one for my personal use from reading a very large number of old documents.
There are a few books about the history of programming languages, but they are mostly descriptive and contain little comparative analysis of the value of various features. Moreover because due to space limitations they have to mention only a part of the features of the languages that are described, the choice of what to mention and what to ignore in the description of a language may be not the most appropriate from the POV of an experienced programmer.
Therefore I have found more useful the original manuals of various programming languages, from when they have been launched, during the fifties, sixties and seventies of the 20th century. After 1980 the pace of innovations has slowed a lot and most of the languages that introduced those innovations are still used today, so finding documentation about them is no longer difficult.
While C was itself an innovative language, the fact that it included very few of the features of the languages contemporaneous with it ensured that many of those features are not available even today in mainstream languages, while other features have become widely used only after decades after their invention, for instance iterators have already been introduced in 1974, in the same year with C, but they have been revived only a quarter of a century later.
Fortunately now there are a handful of Internet sites dedicated to the preservation of the history of computers and programming languages, from where it is easy to download most of the old documentation.
That's a lot of intersections! And it's reasonable that we get programming languages with flaws in one or more areas simply because their designers weren't experts in yet another needed field.
I'd be fascinated to see a graph based approach to programming language features, but I'm not sure they're decomposable and independent enough to make it feasible.
Start by listing out programming language qualities that are good/bad/intolerable for each of those other interaction categories, then work that backwards into a set of required features for a desired language.
E.g. X makes writing a Y compiler optimization impossible
On structured iteration programming, it might be a valuable first step/prototype to see if you can generate a fact/association graph from historical discussions about programming language qualities.
Much has been written (and continues to be!) in many high signal:noise sources. And some lower ones.
(1) extracting a relationship graph between all mentioned qualities and (2) generating sentiment analysis on those relationships (even if positive/neutral/negative/unknown) might help bootstrap a more comprehensively-populated version that can then be refined.
Which new languages are you talking about here? The new languages I'm familiar with (which, granted, are mostly from the period between like 2008 and 2013, so maybe you're talking about much newer ones...) all seem very steeped in familiarity of historical languages. For instance, this article mentions Rust's hygienic macros, which were certainly developed with knowledge of Scheme's earlier work on that.
Rust is one of the typical examples that I had in mind.
While it has some very nice features, they are combined with ugly mistakes inherited from languages such as C.
For instance the need of using the address-of operator for function parameters passed by reference is ugly and pointless.
Moreover, the distinction between parameters passed by value and parameters passed by reference is a big mistake in the design of a programming language.
Already in July 1977, the DoD Ironman requirements for computer programming languages have shown the only right way of classifying function parameters, i.e. in 3 kinds, in, out and in-out (this had been inspired by the language Jovial, previously used in some DoD projects, but Ironman has improved upon the Jovial version of parameter specification).
When any function parameter is specified as belonging to one of these three kinds, then it is irrelevant whether they are passed by value or by reference. That is an implementation choice that belongs to the compiler and which is completely transparent for the programmer (e.g. the programmer does not care whether an in-out parameter is passed by reference, i.e. the function receives a pointer which it uses to modify directly the parameter, or it is passed by value, when the function receives a copy of the initial value of the parameter and at function exit the final value of the copy is copied back to the caller).
Besides the burden imposed on the programmer to care about how parameter passing is implemented, the failure to adhere to the Ironman requirements may cause serious performance degradation.
The ugliest parts of C++, which, especially before 2011, were frequent causes of confusions, bugs and low performance, derive from the failure of C++ (inherited from C) to distinguish the out parameters from the in-out parameters.
To work around this misfeature, which is especially important for an object-oriented language, C++ had to invent some kinds of constructors that would not have been needed otherwise, it also had to handle constructors in some special ways, different from the other kinds of functions, and then it had to continue to invent new tricks for fixing performance problems, like move semantics.
The inappropriate way of classifying function parameters is just one example of many programming language defects that are still widespread even if the right solutions were known fifty years ago.
It would take more space to explain the implications for OOP of distinguishing out parameters from in-out parameters, but the main result is that many redundant operations are avoided automatically and effortlessly, while the C++ compilers have needed several decades of improvements until they have become sophisticated enough to avoid such redundant operations when handling big objects, like in libraries that implement operations with matrices and vectors, and the compilers still need considerable help from the programmer, who has to specify details that should not have been his or her concern.
Rust's design and development was incredibly informed by knowledge of programming language history. People making different judgments and decisions than you would have made is not necessarily evidence that they are less knowledgeable than you, it is often the case that they have just come to different conclusions than you have. That's why there isn't one perfect version of everything; similarly knowledgeable and well-intentioned people often prefer incredibly different answers to the same questions. It's kind of surprising, maybe, but it's also why humanity is so interesting and varied; why we have so much different kinds of art and music and architecture and stuff.
100% of your examples are things that you seem to think have exactly one correct answer, but which people actually have different preferences on and different reasons for choosing different approaches. I promise you that Graydon Hoare was familiar with the concept of in, out, and in-out parameters when he started working on rust.
None of this means that any language is perfect, it's just that you're ascribing things to ignorance that are far more likely to reflect differences of opinion on subjective questions.
I agree that many differences between programming languages are differences between programming styles where it cannot be said that one variant or the other is definitely better, but only that one variant is more suitable for some people while the other variant is more suitable for other people.
Nevertheless, I have been careful to not choose an example of this kind, but one where there is no doubt about what is right and what is wrong.
I am curious how do you justify that it is a good thing for a programmer to waste time to think whether a function should be invoked as f(x) or f(&x) and to waste time and page space with writing and reading "&" symbols, when this wasted time cannot bring any contribution to solving whatever problem needs to be solved.
Choosing whether to pass parameters by value or by reference is a problem of the same nature as the allocation of the hardware registers, which does not belong to a high-level language. The C keyword "register" is now obsolete, but the parameter-passing method should have become obsolete at the same time. Of course, for this to become possible, the direction of the parameters must be specified, to allow a correct compiler decision. When the direction of the parameters is specified, "const" no longer needs to be specified, because it is implied by "in".
Moreover, like I have said, any language that does not distinguish out parameters from in-out parameters, but which nonetheless allows the definition of new data types, together with mechanisms for their allocation, initialization, and deallocation, becomes excessively complicated, because it must add a special kind of functions that support one "out" parameter, i.e. the constructors, despite the fact that normal functions do not support such parameters, and then for each new type several kinds of distinct constructors and operators are needed, e.g. copy constructor, move constructor, assignment, which would not have been needed otherwise. When out and in-out are distinguished, assignment is not an operator, like in C++, but only a syntactic separator. In languages like C++, the programmer also needs to annotate some operations, so that they will use the kinds of implicit constructors that will be efficient in that context.
So if the defined data types can be used in any ways in which primitive types are used, then the mess of C++ is unavoidable. If there are restrictions on how the defined types are used, e.g. when operators cannot be overloaded or when various other such restrictions are enforced, then some of the complications of C++ can be avoided but such restrictions are even less desirable.
So between a single super simple rule of specifying in all function prototypes whether the parameters are in, out or in-out and having to keep in mind a great number of rules of how to deal with constructors in general and with several kinds of special constructors, in order to achieve acceptable performance when handling large objects, I do not see how there can be alternative views about which is right and which is wrong, except when they are based on lack of familiarity with the right solution.
When out and in-out parameters are distinguished, there are no constructors as a special category and no worries about them. An object can be initialized with any function that has an out parameter of the correct type. The only constructor is the default constructor, which is an ordinary function that needs to be specified only so that the compiler will invoke it implicitly when there is no explicit initializer.
> I do not see how there can be alternative views about which is right and which is wrong, except when they are based on lack of familiarity with the right solution.
Well, I understand that you struggle to see how it's possible, but it just is the case that people disagree with you about this stuff, not out of ignorance, but simply by coming to different conclusions than you. I certainly understand that it's frustrating; I have tons of views of all kinds that I think people should see are objective truths, but maddeningly they do not. But that's just how it is!
For what it's worth, if I designed a programming language, you would be a bigger fan of its parameter passing solution. But there would probably be ten other things you wouldn't like! There just isn't a universal truth about this stuff, people have all sorts of different preferences along every axis of language design.
> "Scheme belongs to a list of at least two dozen historical programming languages that have introduced very important innovations".
What are they? Or, where can I go read something more about this? Any tip-offs greatly and sincerely appreciated.
I'm relatively new to programming, and have been focused mostly on Lispy ones (Emacs Lisp, GNU Guile, and most of all Common Lisp), so in browsing the propaganda around these languages, I'm constantly running into things that were allegedly pioneered in Common Lisp in particular.
Would love to read about the 20+ other languages with similar histories of innovation.
I do not have time right now to search for the exhaustive list, but some of the most important old languages that introduced innovations are the following (most programming languages have introduced only one or two important innovations, so if you trace the history of each feature that is present in a modern programming language almost each of them leads to a different old programming language):
Heinz Rutishauser (1951; the first form of the "for" loop);
IBM Preliminary FORTRAN (1954-11-10);
IBM FORTRAN (1956-10-15);
IBM FORTRAN II (1958);
John McCarthy, AIM-001 (1958-09);
John McCarthy, AIM-003 (1958-10);
John McCarthy, AIM-004 (1958-10);
IAL (1958-12);
John McCarthy, AIM-008 (1959-03-13);
LISP I (1960-03-01);
COBOL 60 (1960-04);
ALGOL 60 (1960-05, 1960-06-28);
Edsger Wybe Dijkstra, "Recursive Programming" (1960-05-11);
JOVIAL;
"A Programming Language" (Kenneth E. Iverson, 1962-05);
IBM FORTRAN IV (1963);
CPL (1963-08, 1965-10, 1965-11-29, 1965-12-20);
SNOBOL (1964-01);
BASIC (1964-10-01);
"Definition of new data types in ALGOL x", John McCarthy (1964-10);
IBM NPL (1964-12);
"Record Handling", C. A. R. Hoare (1965-11);
EULER (1966-01);
SNOBOL3 (1966-03-04);
Algol W (1966-06, 1968-03);
IBM PL/I (1966-07);
BCPL (1967-07-21);
PL360 (1968-01);
SIMULA 67 (1968-05);
IBM APL\360 (1968-08);
SNOBOL4 Version 2.0 (1968-10-07);
ALGOL 68 (1968-12-20);
BLISS (1970-01-15; 1971-12);
MACLISP (1970-03-01);
Pascal (1970-11);
B (1972-01-07);
C (1974-01-15);
Alphard (William A. Wulf, 1974-04-30);
SCHEME (1975-12);
Modula (1976-03);
Mesa (1976-10);
IRONMAN (1977-07);
ALGOL 68RS (1977-08);
Alphard (1977-08);
CLU (1977-08);
Mesa Version 3.0 (1977-10);
"Communicating Sequential Processes", C.A.R. Hoare (1978-08);
Icon (1978-09-29, 1979-04);
Ada (1979-06);
Bjarne Stroustrup C with Classes (1980-04, 1982-01);
Modula-2 (1982);
C++ (1984-01-01);
Occam (1985);
ANSI X3.159-1989 "Programming Language C" (which introduced "enum");
Also the language ML and its derivatives are important.
By innovation I mean either a new feature that could not be expressed directly at all in previous languages, for instance CPL has introduced "break", or a new kind of syntax format for expressing a feature that already existed in previous languages, for instance MACLISP has replaced "(QUOTE X)" by "'X". The dates correspond to language versions that introduced innovations, not necessarily to the first version of those languages.
Sounds like you have a programming language in you, if you have opinions on what all the good features are. Might be fun to sketch it out at a minimum.
And yet the list of features given to illustrate that point in the article consists entirely of stuff which is not in fact mainstream:
> Proper tail calls as the fundamental means of iteration is now standard in functional programming. Transformation to continuation-passing style as the basis of compilation likewise. Hygienic macros — now part of Rust and even being considered in Python — were pioneered by researchers who used Scheme as their test bed. That research became part of the Scheme standard in R5RS and R6RS. In functional programming, a lot of research is being done at the moment in algebraic effects, which might revolutionize how side-effects like I/O are done in languages like Haskell — and all this work is based on research into delimited control operators, research which was done by Schemers.
Continuation-passing transforms are very common in compilers. There used to be debates about whether it's better/worse than static single-assignment (SSA), but they later turned out to be formally equivalent :)
The lack of tail-calls in "mainstream" systems (i.e. the ones I get paid to use) is certainly very frustrating. Functional programming ideas are getting more popular, but some of them can be hampered by lack of tail-calls (e.g. using abstractions like Monad is reasonably common in Scala; but naive implementations will cause stack overflows due to the JVM's lack of tail-calls).
Hygenic macros and algebraic effects aren't common in existing languages (although the latter is often provided by libraries; again, I've noticed this creeping into the Scala world), but I've noticed that many (possibly even most) new programming languages have one or both of these. Indeed, regarding algebraic effects: about 10-15 years ago it was common to see languages created for algebraic effects (i.e. someone wanted algebraic effects, so they made a language which has them); whilst these days it's common to see languages "default" to algebraic effects (i.e. someone wants to explore an idea, so they make a language for that; their language does I/O using algebraic effects, because what else are they gonna do‽). I see that as a sort of consensus, that those are the "correct" way to go; if backwards-compatibility isn't an issue.
"Delimited control" is definitely mainstream. Unfortunately most languages seem to implement some weak, ad-hoc form (try/throw, async/await, for/yield, early return); then later have to add more; then more; rather than just implementing the full thing to begin with (e.g. shift/reset, or equivalent).
Heh, I notice that many languages/features are described using the word "modern". I'm not entirely sure what the intended meaning is, but it seems to mean "something that's been well understood since the 1970s, but isn't done by C, Java or Python". Indeed, it's crazy that Scheme, C, ML, Prolog, Smalltalk, etc. were all developed around the same time; yet C is apparently the one we went with...
It now appears like many programming languages (Java, Dart, Swift, etc.) are gradually evolving into ML, e.g. introducing "modern features" like sum types, ADTs, pattern-matching, module systems, etc. (I'm less familiar with C#, but it seems like that's being dragged into the "modern" era by F#)
I mean, from the perspective of the time, was anything other than C really an option?
It was a time when computers were outrageously compute and memory limited, and it was the only language that didn’t have huge (at the time) compute and/or memory costs associated with using it. We weren’t ready for higher level languages at the time.
People developing languages have the same computers as everybody else. It’s only a slight oversimplification to say that C won because Unix won. There were plenty of usable high-level languages, not to mention plenty of low-level languages besides C.
"WTF" reads petulantly and doesn't suit the subsequent piece at all. I had the strange experience of seeing the title and saying to myself, uh-oh, this will be some gossipy thing, I'll read it anyway just in case it also has some good information on the situation, and maybe I'll be proven wrong too.
Which I very happily was! The tone was mature, considered, informative, witty, etc. So I was thrilled to have read it, and more perplexed as to why/how the title ended up there.
Now, this is obviously in the purely-subjective category. Perhaps it reads very normally to others, and I just haven't managed to accept how normal "WTF" has become. Just thought I'd share my personal reaction, because the piece was otherwise outstanding, and honest feedback is presumably hard to get.
I was thrilled/appalled to learn about the ".io" domain too from perusing further the author's site; great to learn that. Hats off.
Is this an currently escalating tendency or has it always been like this? I mean the human nature has always been there but is our current social media landscape amplifying it to 11?
The issue here is that the current generation of programmers seem to want political power more than doing actual work. Everyone and their grandmother saw the huge decline in quality between r5rs and r7rs. The people who wanted to do stuff created racket. The people who want to rule a graveyard stuck with the rnrs process.
Seeing this happen to scheme whose whole purpose is the ability to create languages is bizarre. I can understand a fight like this over gcc or Linux,a fork there isn't something a single person can do as a weekend job. Scheme on the other had is absolutely something a single dev can design and implement in a few months worth of weekends.
Somewhat related, I think, is the argument about 'victimhood culture'. https://en.wikipedia.org/wiki/The_Rise_of_Victimhood_Culture (The Wikipedia article doesn't cite any criticism, unfortunately). Roughly, it argues that society has progressed from "up to victim to restore their own honour" to "society restores dignity to all". And then 'victimhood culture' would be an over-correction where people seek to be victims.
I could believe that society has changed the way it approaches conflict. But, I'd think social media stuff changes the shape of traffic/attention.
I would think people are not inclined to be curious to hear both sides of some conflict.
Common Lisp committee did just fine: produced one massive standard which has been a lasting foundation for three decades since, and dissolved. Certain popular non-Lispy languages have standards committees too with generally positive effects.
> After the release of Miranda by Research Software Ltd. in 1985, interest in lazy functional languages grew. By 1987, more than a dozen non-strict, purely functional programming languages existed. Miranda was the most widely used, but it was proprietary software. At the conference on Functional Programming Languages and Computer Architecture (FPCA '87) in Portland, Oregon, there was a strong consensus that a committee be formed to define an open standard for such languages. The committee's purpose was to consolidate existing functional languages into a common one to serve as a basis for future research in functional-language design. [...]
In my experience, real-world Haskell requires enabling half a dozen language extensions, most of which have only been implemented for GHC and some of which are mutually incompatible.
Over the years Haskell has evolved from a well-defined language into "anything people managed to get merged into Haskell". Learning it is now an absolute nightmare as you are basically required to understand a dozen poorly-documented extensions to use even very basic libraries.
> Learning it is now an absolute nightmare as you are basically required to understand a dozen poorly-documented extensions to use even very basic libraries
Many extensions make Haskell easier to learn since they remove unnecessary restrictions imposed by the standard.
I haven't used Haskell in a few years, but back then one which really bit me was DeriveAnyClass (introduced in 7.10.1) and GeneralizedNewtypeDeriving (introduced in 6.8.1).
Until the introduction of DerivingVia in 8.6.1 it was impossible to tell Haskell which deriving strategy to use - which is extremely annoying when one library requires the one, another library requires the other, and due to issues with orphan instances you have to use them in the same file.
> Many extensions make Haskell easier to learn since they remove unnecessary restrictions imposed by the standard.
I completely agree, and Haskell 2010 is obviously lacking. Unfortunately, there are also a lot of extensions which people have abused to their fullest extent to create completely unreadable code.
Things like UnicodeSyntax and all the syntax overloading are heavily overused, and you simply can't expect a beginner to immediately grok phrases like "if OverloadedLabels and MagicHash are both enabled then x#y means x# y, but if only OverloadedLabels is enabled then it means x #y".
And of course there's the whole cuckoo's egg of Template Haskell just waiting to blow up in your face...
I have to nitpick a bit though. You said "real-world Haskell requires enabling half a dozen language extensions ... some of which are mutually incompatible". 8.6.1 is 5 years old at this point, so the conflict between DeriveAnyClass and GeneralizedNewtypeDeriving is hardly a pertinent example. The conflict between OverloadedLabels and MagicHash is even less of an example, because the average real world Haskeller never uses MagicHash (and rarely OverloadedLabels: I've never used it once in 10 years of professional real world Haskelling).
Perhaps, but if you take the "single person with strong control" path then I think you tend to get a different set of failure cases instead. For a small language, a benevolent-dictator can (unintentionally, through bad decisions) take the language in a direction that just causes it to dwindle into insignificance. For a large language, they can make mis-steps with big consequences (maybe the Python 2->3 transition counts here?).
Sometimes what an established language needs is mostly "write things down, keep all the implementations moving in the same direction, don't make big changes or break compatibility", and in that situation a committee can be a good approach. I think the C standards committee have overall done a reasonable job within that kind of remit.
It's also about what Scheme is and isn't, and what it certainly isn't is another C.
A BDFL would attract a certain following, but some people are not interested in following anyone in particular - and Scheme is there for them.
I'm 100% certain that if a single Scheme implementation emerged as dominant, and appointed a single BDFL in charge of producing all blessed RnRSs & SRFIs, the remaining Scheme implementations/community would continue to do their own thing, because that's what Scheme is about. (See the general response to R6RS as an example.)
I dunno, maybe the python 2 -> 3 transition itself didn't go great, but I think python 3 has been a better foundation for python's wild popularity explosion over the last decade. I think if anything it's (in my opinion) more an example of not pulling the band-aid off quickly enough, rather than an example of why you shouldn't break backwards compatibility.
I'm starting to think that just doing a set of "stdlib" packages for R6RS that roughly matches what Python has (including essentials like SQLite, HTTP servers and requests and JSON/EDN support, together with some common patterns) would have been easier and actually gotten somewhere by now :)
I disagree. Good design comes from discussions and review. There isn't one person who comes up with great designs. Rather, experience shows it is the other way around. Don't get me wrong, when you have a project which needs to advance quickly to a target, having a strong leader who can make final decisions is of course very beneficial. But that is not the case here. The basic language is designed (and has been for decades). The challenge now is to design a language standard which will need to have good community support. Which requires buy-in from enough participants. Of course, even a standards commitee can and should have a strong leader and we are discussing exactly that position. But only a leader, not a dictator, however benevolent that might be.
Someone should create a steering committee tasked with forming a working group to explore a formal standard describing the process of open source project bifurcation and stagnation.
That someone would likely suggest that the venue for that committee's first meeting would be a reclusive island somewhere in the Pacific with an unlimited supply of pina coladas.
> An informal group evolved the language through revisions known as RnRS (a mathematical pun with a reference to Algol hidden in it),
In case anyone else is wondering what the pun was in the name R^nRS. I didn't manage to find anything on google (may have used the wrong keywords), but GPT powered phind.com does manage to find an answer:
> The pun in RnRS, in reference to Algol, is a play on words involving the names of the two programming languages, Scheme and Algol. RnRS stands for "Revised^n Report on the Algorithmic Language Scheme", where n is the version number of the report.
> The pun is a reference to the Algol programming language, which stands for "Algorithmic Language". The naming of Scheme's standards as RnRS is a nod to the "Revised Report on the Algorithmic Language Algol 60", which was the standard defining the Algol 60 programming language.
> The hidden pun is in the "Revised^n" part of RnRS. In mathematics, "^" is an operator that denotes exponentiation. So "Revised^n" could be interpreted as "Revised Revised ... Revised", with the word "Revised" repeated n times. This is a playful way of indicating that the language Scheme has been revised multiple times, similar to how Algol was revised to create Algol 60
I couldn't find any direct quote linking the two so I though it was interesting that it still managed to find the link. All I did was paste the quote into the search engine and ask "What's the pun?".
It listed sources for all statements, but not for the link between them which it either got from an unlisted source or is something it came up with on its own.
Well, that's on the author for claiming it is a pun, personally I'm happy about the explanation that:
- R^nRS is short for Revised^n Report on the Algorithmic Language Scheme
- Algol60 was defined in a document called Revised Report on the Algorithmic Language ALGOL 60
- Revised^n is mathematical notation for Revised Revised ... Revised n-times.
The prose is a bit iffy, but I'm just happy it managed to find the link between RnRS and Algol 60. It's something I didn't manage to find with just google.
https://news.ycombinator.com/item?id=37164243