 You can start. Oh, yeah. OK. Thank you. Hi. Thanks for coming to my talk. I appreciate that. This is two small talks in one talk. We just made a new major release of Gile 3. So I'm very happy and relieved and tired. And so we wanted to celebrate that a little bit. And then also I was thinking about some minimal languages. Obviously the theme of the room. Gile is minimal, but it's old. And old things have a tendency to grow or in size. Or how do you maintain a minimal aspect through time? And in fact, how do you maintain a thing through time? Like what have you observed? So I have a second mini talk, which is things I've learned as being part of Gile over the last, I don't know, 15 years or so. OK. So Gile, I describe it as something which is old, which is ancient, but which is also spry. And spry meaning that it's active and lively. And we just had a new release. And to show that, I can start with some micro benchmarks, which you obviously can't see. But this is comparing Gile 3 versus Gile 2. And the more paint is on there, the better Gile is than Gile 2. If you see some very thin white lines there, each one of those light lines is two times more. So more than half of the benchmarks are more than two times as fast. And one of them, for some reason, is 32 times as fast. So it's a micro benchmark result. And if I take a larger, more real result, perhaps, this is benchmarking a function we have in Gile, which is the core of the interpreter called eval. This is eval that you learn in SICKP or things like this. It used to be in Gile 1.8 when I started with Gile about 15 years ago or so. This function was implemented in C. And we changed to have it implemented in Scheme when we added the compiler. And we did that at first. Things slowed down quite a bit because our compiler wasn't that great, and our virtual machine wasn't that great. It had a number of advantages. But as you can see, we went from computing the 30th Fibonacci number using the interpreter from .32 seconds up to 4 and 1 half seconds. Again, a log scale on the y-axis here. But as we've improved from Gile 2.0 to 2.2 and now finally to 3.0, we're back with the performance of primitive eval, except now it's implemented in Scheme, whereas before it was implemented in C. So I'm pretty pleased with that effect. And if we take a larger benchmark, like something like Geeks, Geeks the package manager written in Gile, on some tasks it can improve by, I don't know, 5%, 10%, 15% perhaps. Depends on how much other things you're doing, I.O. or things like this. So I think pretty good. But when I actually looked up the definition of spry, I didn't say just active and lively. It said, especially of an old person, which is funny, because it's the word I thought about to describe Gile. And then I was offended by the definition. And then I realized, this is reality, and I should just accept it. I turned 40 last week also, so this is related. So Gile is real old. It's not on the upswing like a new language like Rust, for example. It's not Elixir. It's old. It's older than probably some people had fuzz them. It's older than JavaScript, which I realized today was 3 to the power of 3 years before Gile 3. And it's built from old things. Gile itself was not built out of nothing. I wasn't around then. Obviously, I was 13. Gile was built on top of an implementation called SCM, which itself was built on top of an invitation called Scheme in One Defund, S-I-O-D, in 1988, before Bash, before TECL. And these things themselves are built on old things, like their implementations of Scheme, which is old, old, old, which is like a dialect of LISP, which is old, which is an implementation of the lambda calculus, which was 3 to the power of 4 years ago. So what you have after evolving with time, the world is not the same as it was 81 years ago or 27 years ago, nor 9, nor 3. We have to keep adapting. So how do you maintain a minimal system as we go forward in time? And I thought about this, and I understand history as a dialectic process. You have tensions between things. And when you resolve these tensions, you're not ping-pong back and forth between stability and change or between minimalism and complete everything's included. When you resolve these tensions, you get to another state and you have new tensions. And these tensions drive forward the motor of history. And as Mark says, men make their own history, men, you know, language of the day. But they do not make it as they please. They do not make it under self-selected circumstances, but under circumstances existing already. And we're always building on things before. We're never creating new minimal things out of nothing. We're always advancing from where we find ourselves. And then once we've advanced, we find ourselves somewhere else. And so we have to keep on moving on. And so how do we remain minimal over time? And in the context of Gile, what are the tensions that operate? And how do these tensions turn the crank of history, moving Gile forward to a newer and newer state in time? And is it going forward or backwards? But it's certainly developing. And so I see the tensions as the problems in the world. Like the web was not a thing in 1936 or whatever. Between having something stable that you want to build on versus having something that can change and adapt to new circumstances or can improve itself. And there are a number of other tensions in here. And so in this talk, I want to give some observations that I have on how these tensions operate and pull language forward. So one of the lessons I've taken from Gile is that as far as an optimization pattern, often you think about where you are, and how can I get immediately better? You take a step in that direction. This is hill climbing. And it's widely known that you're not going to find necessarily a global maximum if hill climbing is your approach for language development or for program maintenance or anything. Because you could climb and you could reach one hill, but there could be other hills that you don't know of. And if you knew of them, obviously you'd be on them already. But you just can't know because you only see what's directly around you. And so as a language evolution strategy, I find hill climbing is a recipe for death in the end. Because if you ever reach that maximum, you have nowhere to go. You're stopped. Why would you step down in anywhere that's worse? And so I found that's where Gile was 15 years ago or so. We had optimized and optimized and optimized to be a scheme that you would link to from C. And we got to what we thought was a good position, but it had a lot of downsides that we couldn't reach through incremental steps to getting somewhere else. So while hill climbing has its places, it's not enough. Another thing I learned in Gile is that users stay with you unless you push them away, just out of inertia. They'll bleed off through time. You'll get new users through other processes, possibly. But the real exodus events are when you break things for users. And the way to break things for users is to change your interface. And there are many kinds of interfaces. It's the I and API and CLI and ABI. Whenever you change any of these things, you're going to lose some of your users. So one lesson here can be, don't change. Never change, which is not a way to move forward with your users. Obviously not the whole thing, but if we take some examples, Python 3 obviously has some problems in this nature that I'm not expert on, but I certainly know they exist. Gile had a thing in 1.8 called local eval where you could evaluate an expression within a lexical context at runtime. We took it away because we thought that when we added a compiler that we couldn't support this feature. But it turned out to be so important that we had to figure out how to add it back. Otherwise we were just going to lose some important users. We have some, there is some scheme itself is evolving in different ways. And one of the evolutions was a standard called R6RS, which includes some syntax which conflicts with old Gile syntax. And we don't really have a good way to deprecate syntax. So we don't have the R6RS syntax on by default right now. And finally, you would not design Gile today with mutable pairs. You wouldn't design it with set. You wouldn't design it with mutable variables. That would not be what you would design. It's not necessary. There are other ways to do it. They're much more clear. But we have them around because if we took them away, our users would go away. At the same time, you can't keep all your users. You're going to change. So if you can't take the previous strategy of never changing anything, then you have to say, well, OK, sometimes you have to lose some users. And so you need to know when that's OK. And it's hard to know. We've made mistakes in the past. But if you don't change at all, you will reach one of these local maxima and your project will be dead. So for example, we switched in Gile from in 1.8. We had this thing where macros, which are user extensions to program syntax, would be expanded the first time a bit of code was evaluated. And it had a number of downsides. And we switched to an approach where the whole program was expanded before anything was evaluated. Well, you could evaluate during expansion, but that was a separate concern. And that's when we switched to P syntax. And so that if you have side effects when you are expanding in your macros, then they run at a different time when you do expansion eagerly versus lazily. And we switched to this eager expansion. And it broke some things for people. But I think the benefits were enough that we got a more expressive system that we were able to recoup the users on the other side. But it was pain. Similarly, we switched to Garbage Collector. We currently use the Bohm Collector, which is pretty weird for modern language. But it works really well for some uses. One issue is that it runs generally you don't need to tell it how to trace your object graph. But when you do need to tell it how to do it, it's very tricky. And so users which had used this API that allowed them to augment the Garbage Collector's notion of what is live had a really hard problem and still have a really hard problem migrating off of Gal 1.8, which is how this last release 12 years ago or so. The introduction of a notion of compile time is, in a way, runs against minimalism in some ways. And it complicates things for users. But we did that. And also, we did the Python 3 thing more or less of using Unicode for strings. And I think it was the right choice, but it was painful for some people. So every interface you have is cost as maintainer. And I hope that these lessons, I speak from a Gal perspective, but I think that every library author, every program author creates a language of some sort for their users. So the things I say I think should apply for other programs as well, that in Gal, we have a binary API. And so the easiest way to do this is just to minimize your entire interface. So for example, Go does not have a dynamic linking interface because it statically compiles. It's a great radical solution of this, I think. Geeks has a mono repo. Mono repos are also a great solution because you don't have too much of an interface between what you work on and separate things that develop at different paces. Recently, we released Gal 3.0. And I forgot to make some functions exported in the C API. And now I get bug reports. I'm going to have to add them back. There's unintentional change. But this thing that was just out there that was providing no value, I accidentally broke it and now I have to do some work. I don't like doing work. So anyway, the pattern we have in Gal for allowing us to change, allowing us to evolve and change, is parallel installation. And this applies everywhere that we change. This is the pattern. So for example, we have libgal-2.0.so, which you can install in parallel on your system with libgal-3.0. And this philosophy was outlined almost 20 years ago by Havoc Pennington in this great article. If you haven't read it yet, I definitely suggest you take a look at it. And the corollary of this is that you cannot change the meaning of any existing interface. You can extend the meaning. You can add new meaning that doesn't conflict with old meaning. But if you ever change the meaning of something, you need to add a new name. You need to give it a new name, whether it's Gal 3.0 versus Gal 2.0. But in the case, we had a function called make struct. It took this extra argument to support something that was deprecated. This was a very low-level facility. We weren't able to actually adapt this function. So I had to make a replacement called make struct no tail. At some point, I'll deprecate make struct and maybe swap them back. But it takes a long time to do this. Additionally, we have M4 macros that can allow you to say, actually, don't move me to Gal 3. Keep me on Gal 2.2, because I can't. I don't have time to deal with this right now. And as a counter example, libtool does not help us at all. I don't know what it gives you. The libtool versioning has never solved any problem for me. I mean, maybe it's good for distros, but it doesn't help me evolve Gal. We can talk about that. The thing that facilitates change, though, is so you add a new name. You want people to migrate to the new name. Our pattern that we use here is deprecation. And it applies everywhere. On the CAPI, you can add attribute deprecated functions, types, variables. And you can give a message when you do this to say, please migrate to this. So when the users who are capable of making this change compile this, they see this message as like, oh, man. It's giving me all these warnings. I got to get on this. If they decide to switch to the new API, right? You have to give them time. We have this also in Scheme, which, if you run your program at the end, it might print out, you use some deprecated features. Please set an environment variable to let you tell us, we'll tell you more about these features when they come. And we have facilities to kind of if-def them out if you're running in a configuration where you disable deprecation. Because you want to know, is my program compatible with the future? Can I migrate at my own pace? So that gets to our arch pattern of maintaining minimality, which is, OK, you need to change something. You replace it with something new. You deprecate the old thing and ask people to migrate over. And then eventually remove. And remove is important because otherwise you're maximal. You're not minimal, right? So this allows us to change anything. And the only question here is, how long do we have to keep something deprecated? And for some things, they might stay deprecated for 10 years. It depends on how active your user community is, like how what their change philosophy is. Gail coming from kind of old GNU perspective, we're definitely starting from a like, oh, don't change things point of view. But generally for Gail interfaces, we'll go, we'll deprecate something. And let's say we deprecate in 3.0. When 3.2 comes out, we'll remove the deprecated interface. So you can only migrate on to 3.2, which presumably provides you with better things once you remove the old thing. So makestruct is still around with this crappy interface. We just migrated to C99. I'm so ashamed to admit it off of C89. Yeah. So we had the like, defines for UN8 that are now obviously replaced by standard int now from 20 years ago. This thing is still in Gail. But now it gives you a deprecation warning. And we'll remove it next time. And sometimes we don't succeed. Maybe the replacement didn't take into account the use cases of users. So I added something called foreign objects to replace a facility in Gail where you can extend Gail with new types that are backed by some C implementation. And it hasn't been a real success. So I don't know if we'll be able to actually migrate off or if I'm going to have to remove foreign objects, or I don't know exactly. Anyway, so once you change, you reach a new stable point. So for me, unstable series and stable series are also part of this pattern. You get to this stable point 3.0, right? I haven't looked at the mailing list since 3.0 came out because I can't change some things. I'll have to fix the bugs. But users are going to be able to rely on 3.0 being the same for some number of years at this point. I also didn't look at mailing list because I went on holiday, but unrelated. That in a stable series, you yourself have dependencies. And they must share your same ideals with regards to stability and change. So we can't depend on anything that isn't as stable as Gail's goals. Whereas if you're like a new minimal language that comes out in 2020, you might be able to depend on stuff that's very fast-changing because you haven't built up the users yet to be change averse. They're still active in your programs. And if you change something in six months or 12 months, then maybe you can take your users with you. But as you get older, we definitely need more stability. So we only depend on very, very stable things. With the exception of GNU-Lib, which is one of these include-only libraries, which has helped us to migrate forward without much risk of change there. Right. So the other thing is that as a author of a language, be that language at library or Gail or whatever, you're going to get pressures for users, for you to add things, to make your language bigger, to add features or what have you. And in Gail, being based on Scheme, I found that it's been already really good that we empower our users that the language constructs they create sit on a first-class level with the language constructs that are defined in Gail. So users obviously can extend Gail with modules and such. But I think that the syntax question is really important as well. We all know the problems that come in extending the syntax of JavaScript or Python or something like this, because syntax keywords are a first-class part of the language that users don't really have access to. Whereas in Scheme, we're able to evolve this syntax without my permission, my permission as Gail co-mentainer. The other thing is that often you have maybe a performance difference, especially in language implementations. A user defines some facility. It doesn't perform as well as something implemented in Gail because Gail has access to all the speed, shortcuts, and things like this. And this is a real problem we had in 1.8. It pushed users towards a overuse of C extensions. It added to our interface there. We still have a lot of that interface that we still have to maintain. And having made this leap to have a better compiler and runtime system, users can now define things in Scheme that are perfectly acceptable generally. So my lesson here is that if you empower your users to create things that sit on as peers to the facilities to find in the language, then they need less from you, which is good because I don't want to do things. Speaking of that, when I do things, users come with code sometimes. And they want you to take it, an extension to your software, the software you're responsible for. And this is almost always a burden. And so for me, I really prefer to empower the users, separate modules, sort of things, except when the feature is just quite stable and it's not going to demand anything of me. But that involves a commitment from the users as well. Like they have to realize that this thing they're adding is stable or participate actively in Gail maintenance. So you just need some agreement there. And so some things we have in Gail are in because they're super stable and some things are out because fibers, for example, is a lightweight concurrency facility. I haven't added it to Gail because I don't know if it's stable enough, I don't know. Right, just a couple more things. The greatest problem we have in Gail right now with regards to evolution is memory management on the C interface. It would be really nice for us to have a precise, generational garbage collector. Instead, we use a sometimes precise, sometimes conservative details, the Bohm collector, which doesn't offer bump pointer generational allocation. If you look at the micro benchmarks, the way to get the next bump to reach Shea scheme is to have a good garbage collector. But it's very difficult on the C level to have all of your users change this API in a very fundamental way. I'm not really sure how we're gonna get there. Gail's not the only language with these problems even. PyPy's has issues with regards to when the delete finalizer gets called or destructor or whatever it is in Python relative to CPython, which is where it happens at a different time. And if we look in the future though, so we're around in the circle. We've gone from stable to change and now we're back to stable again. And in Gail's perspective, I wanna add another dimension to parallel installability. Users can do most anything they want with Gail defining new constructs that are on a par with Gail's constructs, with the exception of whatever language it is at the top of your file, right? At the top of your file, it's still kind of this default Gail environment, this default Gail syntax. And Racket has this really wonderful, elegant solution to this, which is called hashlang. You just sort of note what language your file is in at the top and you can have pretty significantly different syntaxes. You can have a more friendly perhaps approach to using library. So I wanna have that. I find that to be parallel installability for source languages, even source languages that are radically different than Scheme. And additionally, I've been working in the weeds, I haven't been working so much on the higher linguistic levels. And I think Racket has shown a lot of idioms that are really useful that we would like to set them down into Gail's riverbed. And so the thing about the dialectic is it doesn't advance Gail itself. There's still people doing things. And until now it's been me sort of cranking around from stable release to development cycle and then back to stable release. And I want to see if I can step back and let other people hold that some more. Whether that's like, I put a lot of effort into community or if I just step back for a year to see what happens, I'm not sure what's gonna do it. But one of those things will work. So yeah, that's my thoughts on evolving language. Questions, welcome. Yes, in the back. I think, that's a very good question. The question is, when is it a good idea to lose users? From the selfish perspective of Gail, it's better to lose users if you recoup more users than you lost. But you don't recoup new users tomorrow, right? It's a function that you expect that it's going to come in at some point in time. So is it better if you lose 10% of your users but you get 20% more in three years or does it need to be six months? It really depends on your goals as far as your growth, right? If you expect to grow a lot, then you can lose all your users, right? I think Gail's more on a smaller curve. Upwards, I hope. Yes. Is it using auto tools? The question is, is Gail still using auto tools? Yes, it does. It's horrible in ways I understand. I don't have any particular plans to switch off, but if somebody wants to give a crack at that, that could be interesting. But I would note also that I got bug reports after at least Gail from itanium people and HPPA systems. And so, is it okay to lose those users? I don't know, I don't know. Yeah, it's fine, yeah. I can't let other people continue. But do you remember in some of your previous, either a talk or a blog post that you work best alone? So how do you see that? So the question is, I would like evidently to allow other people to facilitate other people contribute. But I've said previously that I work best alone, which is true. I don't know what the resolution there is. I don't know. Right now it's a desire without a plan. Okay, thank you very much.