First of all, there came the change in name. This programming language used to be called Coral, but the knowledge that there is another language that shares a part of that name (CORAL 66) kept bugging me. Afterall, I’m not Apple Inc., I can’t afford to name a language the exact same name as they did with Swift (yes, there are now two languages with the exact same name). And short names of gemstones are already occupied by Ruby. So I kept thinking over the time while working on now-former-Coral, and finally came up with an idea: Gear. A tiny wheel inside a clockwork, that gives the machine life. I thought that was pretty cool, and one letter shorter than Coral. It took just a few hours to rename the whole language, in this early phase. Then the author of
a language of the same name contacted me and I abandoned the name Gear in favour of Amlantis, shorter Aml, which has some properties that the name Gear did not have.
But, the early phase is slowly getting over, and the language matures very fast.
A few examples of the changes that were made in the past few days:
Comments
Comments are pretty important, no matter what your clean code teacher tells you. They express ideas that are not expressed from interfaces. Comments are like embedded metadata of your source code. If you have more such metadata than actual source code, you might be doing it wrong though. Anyway, there were recently three changes regarding syntax of comments. First, /*
and */
multi-line comments were replaced with OCaml-y (*
and *)
, and then single-line comments went from //
to ;;
to no single-line comments at all! Why? Because I realized I needed ;;
for something else, and the inspiration from Lisps, where ;
introduces all comments, just was not enough. Also, I needed //
for an operator. Now comments are distinct, there is just one kind of comments (plus their documentation version (*!
and *)
. Moreover, it’s now pretty easy to turn a single-line comment into a multi-line comment, because, well, the single-line comment is already in a multi-line comment syntax. Yay!
Workflows
This is a change that is in fact still in progress. I realized there were some parts of it in the former version that just did not quite do what they were supposed to do, so I went to the source idea in F# and discussed its specification on what its workflows do (called computation expressions though). And now I’m redoing the whole thing for Aml. Unfortunately for me, F# has some syntax goodies that Aml does not have, and also Aml has some syntaxes that F# obviously does not have, and I have to decide where the workflows will be applied and how. The very difference between workflows in Aml and computation expressions in F# is that Aml has to be able to do the translations in runtime via runtime macros.
Vendors & Modules
I always wanted to make this part right, but could not quite get it right in Coral. Now, with Aml, I thought really hard about what the real use cases could really be, and finally decided for a change in Aml’s syntax regarding modules and vendors. See, a vendor in Aml’s context is somebody, or some organization, that ships its modules to other users, either in compiled binary forms, source forms, or maybe even both. And a module could be a lot of things – it could be just a library, a utility, a complex GUI application, or even combinations of those. Originally, I thought it would be necessary to distinguish a module, vendor and a class in any path. So I came up with the Module~[Vendor].Class
syntax, where the vendor just added some extra property to the module name and then the language could just list the referenced modules in some place using their full name including vendors, and then use the simple name everywhere else. But that syntax is pretty cumbersome, lengthy, not really easy to write fast. So I thought… well, on Packagist or GitHub, "modules" are referred to using something like Vendor/Module
, why not use that? A slash character was already an allowed token within an identifier, so that would only lead to deletion of the extra syntax and the rest would be the same. And then I realized, that while a slash character is indeed allowed in identifiers, it is allowed in ALL identifiers, not just module names, but classes, functions, methods, damn, even variables, wouldn’t that be a problem in name resolution? And then I finally realized that not any more problem than it already had been. Module names including their vendors will already be imported from the module definition, other places must obviously import the name themselves, the language can’t do that for them.
Syntactic Forms
Again, another syntax that was borrowed from Lisps, and did not quite fit into Aml. But it survived, just with a different set of delimiters: <@ quasi-quote @>
and <@@ quote @@>
. I kept a simple syntax to ''quote
expressions that are easily quoted for now. The new syntax fits into other similar syntaxes that need to draw attention – like goto <<labels>>
, which is from Ada, just customized.
Multi-purpose Yield
I spent quite some time thinking about this, and came to realize it was necessary. Originally, yield
was supposed to do what it does in Ruby – pass arguments to a block (basically a lambda) that was given to the invoked function. But, it had some extra usage in generator expressions and loops. Also, there is this Fiber.yield
thing that is planned for Aml, and indeed it does again what it does in Ruby, maybe even more. So that makes up three different purposes for the yield
keyword, if we ignore things like Thread.yield
, which do not accept any value, and might have outcome somewhat similar to Fiber.yield
, but not quite the same. Then, I quickly tested what yield does in Ruby, if no block was given. It is an error condition. Ha! Then it was easy to write down a list of simple runtime-checkable rules that would apply to usage of yield in the three different cases. Just search for yield expressions in the TOC of Aml’s specification. But, it did not went without trouble – workflows. What if, say, we use a function that yields for a loop, but that function would be used within a workflow expression? I checked what F# has to say about that in the specification, and it seems that for the seq<_>
thing, it would happily ignore the yield hidden behind a function call, since, well, yield does not do quite the same thing as in Aml or Ruby when standalone. That’s when this tweet came that I realized how to solve that: ignore the function call only if it does not yield – and implement the yield using simply a thrown value. Local method cache-similar mechanisms could ensure that the invoked function remembers it’s execution state (only one frame is needed) and resume it the next time, like if it was encapsulated within a fiber.