Aml - A Modular Language (Progress Update)

Hi all!

A while back I posted about a language I was designing called Aml. It is a language for exposing user-created domain-specific languages. Currently the language's implementation is far along enough to start being worth taking a look at and critiquing its semantics. Please take a look at it here - The open source repository is here - Feel free to critique anything about it. Hate my code? Terrible use of an ML-derived language? Retarded syntax or semantics? It's all welcome, please!

Aml itself is a lisp-style language with a lot of foundational functional programming features, as well as basic imperative features (for the rare times you might need them). You can augment Aml with your own domain-specific language semantics by writing an Aml Language Module in F#.

Aml was designed to solve the problem of having to write each domain-specific language from scratch. It includes all the features you would expect from a stripped-down general purpose language, and allows you to write only the 'sub-interpreter' (AKA, Aml Language Module) needed to interpret code specific to your domain.

One could say that lisp already solves this problem, but Aml hopes to do this specific task better than lisp since writing domain specific languages in Aml is done with F# rather than with macros. Also it's deployable to .NET out of the box. In fewer words, Aml is the free foundation you need to get your domain-specific language up and running with absolutely minimal development time on .NET, and without hassling with macro debugging!

The one domain-specific language I've been building for Aml is a declarative object language that uses declarative attributes to implement interactive applications like UIs and simulations. Unfortunately, I made a muck of the original implementation, so it is now being rewritten. So if anyone remembers me talking about DOL, that was it. It is now being rebuilt as DAL (declarative attribute language) and should be a pretty killer little language for games and such once I get it done (probably take another month)... That is, if .NET's garbage collector pauses will be tolerable as not to drop the frame rate intermittently (fingers crossed).

Please take a bit of time to read the AmlSpec.rtf file in the Documentation folder, read the Stdlib Aml code in the Stdlib folder, and run AmlRepl.exe to play with the language. Any feedback at all is welcome, and I would love to answer any questions!

PS -

The best current way to read / edit Aml files is to -

1) Install 'TextPad 7'
2) Copy 'Aml.syn' from the 'aml/Plugins/TextPad' directory to the 'Program Files (x86)/TextPad 7/Samples' directory.
3) In TextPad 7, 'Cofigure' -> 'New Document Class', type 'Aml' for 'Document class name', and finally type '*.aml' for 'Class members'.
4) Restart TextPad 7 and open the Aml files in the 'aml/AmlRepl/AmlRepl/Stdlib' directory.

Voila! Nice syntax highlighting!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Why the deviations from Scheme?

There seems a lot here that's just like Scheme except not quite, which has the effect of making things harder for people who know Scheme without helping non-Lispers all that much. Why not do something like Owl Lisp, which implements a faithful subset of R7RS without mutation (or variadic functions)? You can then enhance that subset with ref cells (proposal for R7RS-large), contracts, protocols, etc. etc.

The differences are large but subtle, I think

The differences between Aml and existing lisps is very large but equally subtle, so it's hard to explain. It may take me a few explanations to get the value proposition across.

To start, Scheme is a general purpose programming language while Aml is not. Aml is a linguistic foundation for building and exposing DSLs. In other words, Aml is a DSL for DSLs. It allows you to build extremely complicated external DSLs like DAL (see Aml documentation) without having to use very unreliable programming techniques like macros, globals, and excessive mutations that pervade lisp DSL implementations. Being able to build your DSLs in F# rather than in lisp is a huge advantage for users. Aml is only there as a convenient means to expose your DSL to the world.

So while there are some surface similarities to Aml and existing lisps, these are not as relevant as might first appear. The deeper difference is in what Aml is meant to do. Rather than be a general purpose language, it is a foundation for building and exposing domain-specific languages.

Another key advantage to Aml is its availability on .NET.

So you can imagine an entire game engine implemented behind Aml as a collection of Language Modules, and the game editor manipulating Aml objects directly, writing out Aml code on save. In my experience as a game engine developer, a game engine is not much more than a meta-DSL that exposes a collection of sub-DSLs. This is what I will eventually create with Dal, the first Aml Language Module I hope to complete. Doing this type of work in Lisp with just macros, global mutable state, and no .NET foundation would be quite harrowing. It would scream, at least to me, the wrong tool for the job.

Er, maybe I didn't answer your question...

Perhaps your question is, why isn't Aml a superset of small Scheme (or some subset)? I suppose the answer is, because I haven't seen clear advantages to doing that. Aml may even evolve to an entirely different syntax (as covered in the AmlSpec doc). The overlap between Aml and lisps generally really is superficial - they just both happen to be based on easy-to-parse s-expressions and use lambda calculus for their semantics, and even the first similarity may be nullified if the syntax changes.

So really, I just don't yet see that much non-surface similarity. It seems the surface similarities may be a red herring to what Aml is really about, IMO.

Knowledge reified in the design

Perhaps your question is, why isn't Aml a superset of small Scheme (or some subset)? I suppose the answer is, because I haven't seen clear advantages to doing that.

Scheme designers have spent a lot of time considering various ways to do things. They eventually picked one that is consistent and stood the test of time. There is actually a lot of knowledge about false starts and better iterations embodied in the current design, and you would be well advised to stick to it wherever that doesn't completely denature your work.

(As a bonus, you've get familiarity among some developers for free. I'm talking about the semantics here, not the syntax.)

Potential misstatement

Perhaps I misstated that I don't see the advantages, I certainly do. I just see costs that outweigh them. What I meant to say is how I don't see it is an advantage overall after factoring in all the downsides. There are enormous upsides to making a superset of an existing language, no doubt. After considering Aml's similarity to lisps as only being of a surface nature, I have decided that the costs of such coupling outweighs the benefits.

Scheme and Aml are like two friends who are often often assumed to be brothers, but turn out to be unrelated. Aml is not a general-purpose language, and it's _not_ an acceptable lisp (though with the requisite Language Modules plugged-in, I suppose it could act like one).

Instead of focusing on possible relationships to Scheme, et al, I would like to steer the conversation toward a more concrete end - such as, if there is some semantics or syntax that you feel Aml got wrong, say, compared to Scheme (or any other language), please highlight it here. More interestingly, what do you think of Aml's unique offering of exposing DSLs written in F#? What could have been done better toward this end?

If possible, I would currently like to steer the conversation towards Aml's semantics and how they help / hinder the intended uses of exposing complex DSLs built with F# and integrated with existing or new .NET applications.