 So, I've been working on a programming language called Animus, and in this video I'm going to give you as quickly as I can a justification for why I'm creating this language. So, in short, Animus is a new dialect of Lisp, and it's based very closely, at least semantically, on a dialect of Lisp called Closure. But at the semantic level, Animus makes a few key changes to make the language more comfortable for people familiar, say, with Python and JavaScript. Most notably, Animus makes it easier for you to program in a more familiar imperative style rather than Closure's usual functional style. Aside from this, Animus makes a few changes that allow for tools to give you some of the conveniences you usually only expect in static languages, like, for example, the ability to reliably do name refactoring. And lastly, another goal of Animus is to do something about language pollution, that is, the proliferation of all sorts of different domain-specific languages. This is one of the more speculative goals of the language, and at this point I'm not going to say much about it. So you're probably wondering, why do I call it Animus? Well, first off, it's spelled Animus with a V, but the V is pronounced U, so just pretend it's Latin. This funny spelling was deliberate so that it will come up uniquely in Google searches. As for the choice of the word Animus, this was mainly a play on Scheme, because Animus is a Latin word having many connotations, and those connotations include liveliness, spiritedness, animatedness, but also intelligence, intention, and planning. And aside from all that, apparently there's a megazord named Animus. So again, Animus is based quite closely on closure, in fact the implementation I'm working on pretty much just lays on top of the closure implementation. So the question is, well, what's wrong with closure? Well, closure is a very nice dialect of Lisbon and has many interesting ideas, which are pretty much all retained in Animus. But the usual complaints apply, the usual complaints about Lisbon. First off, there's all too many parentheses, but then specific closure syntax. You then exchange some of those parentheses you usually have in Lisbon. They get exchanged for square brackets and curly braces. And then also like in all Lisbon, you have these apostrophes floating all over the place for quoting. And then in closure, you also have some syntactical oddities with some special reader syntax involving stuff starting with the number sign and the carrot sign and the at symbol. And then you have some weird business about symbol resolution rules. Symbols containing dot and symbols containing slashes get resolved in special ways, which is a bit complex. Part of that has to do with the way interop with Java is done. And then like in any Lisbon, the indentation style is very hard to read. When you look at the left edge of closure code or any Lisbon dialect, the left edge is very jagged and I think very hard for beginners to pick up on the indentation style. And then like in any closure or dynamic language, you have poor syntax highlighting and other sorts of editor assistances. And as I mentioned briefly, that's something that Animus tries to address. And finally, and this is probably the biggest one, at least semantically, and that is closure really does impose immutability on you. While closure has some very interesting and promising ideas in this area, it's still, I think, too much for programmers coming from, say, Python and JavaScript. There are other advantages that Lisb offers, which would be nice if those programmers could take advantage of without having to totally rethink how they structure their programs from top to bottom. So again, Animus is implemented on top of closure and what gets changed is obviously, first of all, there's a different reader syntax. Some names in the standard library get changed. Some functions in macros, their arguments or their names and their functionality changes just a bit. And also we add some new functions and some new macros and also some new types. And in the underlying compiler, we have to change a few of the special forms and tweak the rules for symbol resolution a bit. So what do programmers get when they come from Python and JavaScript? What about Python and JavaScript actually need to be fixed? Well, the first thing that's obvious to me is that Python and JavaScript just simply have too much syntax and they don't have proper macro support. And on top of that, they don't have decent editor instances like say automatic name refactoring. And as it stands today, Python and JavaScript don't have decent support for threading. In fact, Python doesn't have real support for threading at all. And just very broadly speaking, I've always felt using JavaScript that there are aspects which should be more like Python and using Python, I've always felt there should be aspects which should be more like JavaScript. And in both cases, there are aspects where they should be both more like Lisp. So for example, I've generally felt that the JavaScript type system should be more like the Python type system. And in Python, I missed certain allowances like say, the ability to easily create anonymous functions as expressions in a way that's more flexible than what Lambda in Python allows. And then in both cases, both JavaScript and Python, they both should have Lisp like macros and also they should allow for the control flow style found in functional dialects of Lisp like Closure. So to accommodate the style of programming JavaScript and Python programmers are used to, Animas adds to Closure three basic things. There are closer analogs to the usual control flow structures like while and if LF else. These don't supplant however the traditional Lisp control flow structures. They're all there. They're just these are supplements to them just for familiarity's sake. And more significantly, Animas also has a class mechanism modeled after Python's classes. And finally, probably most importantly, the collection types in Animas are by default actually mutable, rather than immutable. So in Closure, when you create class instances or when you use the square brackets or curly braces to create collections, those are all immutable. And Animas, however, that changes and all those types are mutable by default. If you want to create an immutable collection in Animas, you use the same syntax but you proceed it with a hyphen. Now you may notice I keep using Python and JavaScript as the example dynamic languages whose users I wish to accommodate with Animas. So you're probably wondering, well, what about other languages like Perl and Ruby? Well, in truth, I don't really think there's much difference semantically between Perl, Ruby, JavaScript, Python, PHP, basically all of today's popular dynamic languages. There aren't huge differences really. I just think of Python and JavaScript because those are the dynamic languages I far prefer to the others. And that's because I consider them to have much more sensibly designed type systems and also syntaxes. In my mind, Perl, Ruby, and also Bash represent what I think should be considered a dead end in the programming language design. Specifically, these three languages all go about syntax in entirely the wrong way. A good syntax, I would argue, is these four things. First off, it's easy to learn in the first place. And then once you've learned it, it should be easy to type and edit. And then it should be easy to mentally parse so you can scan it and notice errors quickly. And finally, and perhaps most importantly, it should be easy to reason about. And primarily what that means is that the syntax should have few context-sensitive rules. For example, C has a number of icky context-sensitive rules, such as the meaning of asterisk. Sometimes it's a unary operator in an expression. Sometimes it's a binary operator in an expression. And sometimes it's a unary operator in a declaration, not really an expression. And parsing the difference between those three cases is something every C programmer has to learn. For another example, simply the use of parentheses in a C-style syntax language is very context-sensitive. Sometimes it's used just to surround an expression, and sometimes parans denote a function call. Sometimes they denote a macro call. Sometimes they enclose the condition expression of a control flow structure. And sometimes they're just enclosing the params of a function definition. Or for another example, considering Java use of dot. Most often, dot is used to access a field or to invoke a method. But sometimes it's just used to separate the components of a package name. Sometimes it's used to separate a package from the class, which is inside that package, and so forth. So again, the problem with these context-sensitive rules is that perceiving the different context, it's a skill that's very hard to learn in the first place. And even once you learn it, it's something which is just a mental burden that you constantly have to pay when you read code. When syntax is hard to reason about, the reader has to constantly account for special cases. So what's wrong especially with Bash, Perl, and Ruby? Why are they especially bad syntaxes? Well, on the spectrum of all languages, they fall squarely on the extreme of the hetero-iconic side, whereas Lisp is at the other extreme of the homo-iconic side. In hetero-iconic languages, everything has its own special syntax. And so when we then wish to introduce syntactical conveniences, those things end up being their own special cases. It's an ad hoc system of piling one syntactical convenience upon the other. And then what typically results is a syntax that's very hard to reason about because it's full of all sorts of special cases and weird interactions between the different parts of the syntax. When you have a homo-iconic language like Lisp, however, syntactical conveniences don't have to be introduced as special cases. You can have this one general-purpose mechanism called macros that gets you all the syntactical compression you should want. So this argument suggests that homo-iconic syntax is the ideal in syntax design. But the homo-iconic syntax of Lisp quite famously really does have some serious problems. So what does Animas do to address those problems? Well, the first thing always criticized about Lisp is the preponderance of parentheses. So Animas needs to do something to cut down on the number of parentheses. The other major problem with Lisp syntax, though one not cited nearly as often as the preponderance of parentheses, is the strange indentation style. The key to reading current Lisp syntax with any fluidity at all is to get accustomed to the strange indentation style. But rather than requiring people to adjust to that indentation style, I think it should just be reformed. So it's more sensible in the first place. Another problem I find with Lisp syntax, though this is something not often pointed out, is that the whole system of quoting is first off really quite confusing and secondly is just ugly because it seems to require apostrophes scattered around at seemingly random places in your code. The solution for this problem in Animas is to simply redesign the special forms and the standard macros so that they avoid quoting as much as possible. Finally, the last major problem with Lisp syntax, and this is again something which is not often articulated, but a major thing which makes it hostile to non-Lispers is that Lisp syntax does not have a look. And what I mean by this is that in most languages, programmers become accustomed to look for certain patterns of special symbols and certain patterns in the reserved word highlighting to help them see the outline of their code. In Lisp, you usually don't have this same style of highlighting and the code all looks too uniform to give it any shape. This is the major downside of Homo-Econic syntax. When everything is uniform, everything looks the same so nothing stands out. So the way Animas compensates for this is that it has a unique and much more elaborate system of highlighting than what you're used to in most languages. And as you'll see, this highlighting scheme gives a shape to the code and makes things which need to stand out stand out. So first off, how do we cut back on the number of parentheses? Well, the first thing that has to go is this convention in most Lisp's which is called grouping. Here, for example, is a let form in scheme where the variables being assigned to, having values bound to them, encloses all of those pairs of variables and values in a set of parentheses and then each pair is in self in another pair of parentheses. These parentheses do not, like most other parentheses in Lisp, denote the invocation of a function or macro. Rather, they are just used in this form, this special form let, to group these arguments to pair them up. The x3 in parentheses here is not a function called to some function called x with an argument 3 is just matching the symbol x together with the value 3. So to make this clear, here in orange are parentheses which are being used expressly for grouping purposes. Closure itself actually takes a very sensible step to cut down on this preponderance of grouping parentheses. First of all, in these binding forms, like with let, it uses instead just a single pair of square brackets to enclose all of the pairs rather than enclosing each one in its own pair of parentheses and then enclosing them all in another pair of parentheses. So that right there closures off to good start, but animus actually goes the very last step and simply gets rid of any delimiters whatsoever. The convention in animus is that binding forms like let simply list their symbols with accompanying values and then the body is where the first form that is enclosed in parentheses. So we don't actually need any parentheses or any other delimiter to set them off. The let form can simply infer from the types of the arguments. Now you might object here that looking at this, you can't quickly see which variables are being declared, that everything here just kind of runs together. The answer to that as you'll see is that highlighting fixes that problem. Now grouping is used not just in binding forms, it's used in some other contexts, some other special forms and macros, and the convention there in animus is to simply use a specially designated symbol to delineate the two groups. So here, for example, imagine some macro foo and rather than grouping its arguments into two groups with parentheses, we just delineate them with a specially designated symbol, in this case, bar. So getting rid of the parentheses of grouping goes a long way to cutting down significantly on the number of parentheses, but to go much further, we have rules in animus about implicit parentheses and indentation. First off, to keep everything nice and regular, animus has a strict rule that every line must begin with the number of spaces, which is a multiple of four. So either zero spaces, four spaces, eight spaces, 12 spaces, or so forth. And tab characters are simply not allowed, that's always a parsing error. The other part of this rule is that when we indent something underneath another line, we always indent by just four spaces, never anything else. So you never have situations where you use double indentation to indicate that these lines are actually continuation of something on the interior of the preceding line rather than continuation of what's at the start of the preceding line. Quite deliberately, there's no allowance for that situation. You're always indenting by four spaces. The second rule is that when you have an open delimiter at the start of the line, which is not then closed before the end of the line, then that open delimiter is closed either at the end of that line, or if there are additional lines indented underneath that line, then the delimiter is implicitly closed at the end of what's indented underneath. So consider these three examples. If we have a line reading foo abc starting with two open parins that are not closed by the end of that line, then those open delimiters are implicitly closed at the end of the line. In the second example, we have the same thing, except the open delimiters are first a square bracket than an open parin. And so when they get implicitly closed, they get, of course, closed in the opposite order so that they match up properly. The last example also begins with two open delimiters, except notice that this open parin is explicitly closed with a matching end parin on the same line. That's why the parin is not highlighted. The convention in Animas is to highlight these open delimiters at the start of the line, which are not explicitly closed. In any case, only the curly brace here is left open, and it is implicitly closed not at the end of the line, but because this line has stuff indented underneath, it gets implicitly closed at the end of all that stuff, so it's going to be placed immediately after the four here. Now, for the sake of stylistic uniformity, where these implicit end delimiters are allowed, they're not just optional, they're actually required, so you can't make them explicit. This top example here actually is not legal, it's a parsing error, because you're actually required to leave that end delimiter implicit. Now, the last special rule of indentation and delimiters is that when a line begins with a symbol, that is, a symbol and not some open delimiter or some number, just actually a symbol, then that line implicitly begins with a single open parin, unless that symbol is preceded with a comma. This implicit open parin is then implicitly closed according to our second rule. It gets closed at the end of that line, or any lines indented underneath. So, if you wish to write a line of foo abc enclosed in parins, you don't write foo abc enclosed in parins, nor do you even put simply the open parin at the start, you actually write it with no parins at all. And because this line begins with a symbol, foo is a symbol, then there is an implicit open parin at front, which then is implicitly closed at the end. And then for cases where you wish to write foo abc with no parins at all, you have to write that line starting with a comma to suppress the usual implicit parin. So, if we take an example excerpt of closure code and we apply all of these indentation rules and also apply our changes with grouping, what we end up with is something looking like this, which notice as many fewer parentheses and also fewer other delimiters. Note that this isn't actually proper animus code yet. We're going to make a few more changes as I'll show you. In any case, that example does demonstrate how animus solves the problem of too many parentheses. Now, what about these editor conveniences I was talking about? Well, first consider a typical situation in a dynamic language where you write something like foo.bar with the arguments x and y. If this is code from a dynamic language like Python or JavaScript, then there are many different things that could be going wrong. For example, foo, x and y, those might be variables that don't resolve anything into the current scope and the arguments might not be in the right type or the right order or right number. Bar might not be a method of foo or might not be a member of foo's type at all. And of course, it's always possible that the programmer is making a mistake and simply invoking the wrong method. Maybe bar is not what they mean to invoke here for their purposes. Or of course, it's even possible that even if the programmer gets the types of the arguments right and their order correct and everything, the values themselves may simply be wrong. Because the programmer has made some sort of logic error. The problem in a dynamic language is that these kinds of errors are never caught until the program is actually run. And of course, in some of these cases, the language won't catch the problem at all. You'll just have some kind of weird bug. In a static language in contrast, like, say, Java, most of these problems are fixed or at least fixed in the sense that when the programmer does make these errors, the errors are caught by the compiler. So you don't actually have to run the program to catch most of these errors. Sure, it's possible with dynamic languages to do some very sophisticated kinds of code analysis, type inference and so forth that will, in many cases, catch these errors. But such tools never work 100% reliably and they're not very commonly available and will simply, they just don't work as well. You simply can't get the same kind of consistent assurances in dynamic languages that you can get in a static language like, say, Java. What Animus tries to do that's unique for a dynamic language is it tries to fix at least this first problem, the case where you've made a mistake because you've typed a name that doesn't actually resolve to anything in the current scope. Now, closure actually, by itself, goes a long way towards pretty much solving this problem. In closure, unlike most other dynamic languages, when you use a name, that name, when it then occurs in a function scope, will not be able to solve in a function scope, say, it has to actually resolve to something in the local scope there or it has to resolve to something already defined in the global scope or the module scope, we can call it, the namespace scope. So unlike, say, in Python or JavaScript, when a name is used in a function definition that at that point, at that time of definition doesn't actually resolve anything, that's actually an error. It doesn't work like in Python where the name is only resolved The difference this makes in practice is if I have Python code where a name inside a function is bad, that is, it doesn't actually resolve anything in the module scope, then that program is going to throw an exception when I actually invoke that function and that name is an access of that name is attempted. In closure, however, that bad name is going to trigger an exception when the function itself is defined, not when the function is actually invoked. So in practice, you'll pretty much get that error anytime you start the program. That's very good as far as it goes, but the thing still lacking is that with closure, you can't simply just run your program to discover these errors without actually running your program. The solution to this in Animas is to introduce a second mode of execution or rather what I call a processing phase of the code. Animas introduces a form called run module in which you put a body of code that is only executed when you are truly running the program, so when you write a typical module, you include a bunch of definitions like for functions and so forth, and then probably at the bottom, you include a run module section that's only executed when you are actually running the program. However, though the run module bodies aren't executed, they are compiled. So the processing phase does check at least the basic correctness of the code in your run module blocks. So by compiling the code and executing much of it and actually executing the program, this processing phase, it checks for basic errors, like, say, making sure when you use a name that it exists in that namespace. But a couple other things also happen in this processing phase. For one thing, all of the parsed and compiled code gets preserved in another file, so the norm in Animas is actually like in Python where your code gets preserved as compiled Python object files. So this is a little bit of a closure because closure does allow ahead-of-time compilation, but it's sort of something you have to do manually. It's not just expected by default. The other big thing processing gets us is metadata information about the code. Information that can then be used by text editors to provide a good number of conveniences. The way this works is that the parser and then the special forms in compilation and the macros when they're processed, are in the same mode. This metadata information can then be used in your text editing environment to provide information like, say, clicking on a name and seeing where that name is defined and what it's defined as. This metadata information is what allows your text editor to provide not just syntax highlighting, but different highlights for different symbols depending upon the function of those symbols. The scheme I've devised for highlighting symbols looks like this. The idea is that depending on where a name is defined and what the nature of a name is, whether it's a standard name, a part of the standard library, or if it's something you've defined in that current namespace, or it's defined as a local, or whether it's defined from some other namespace, all those different symbols are going to have different highlights. First, what I call the common names, the things that are all part of the standard library, those are highlighted in blue with the distinction that forms which start a block. Those are both bold and in a slightly different shade of blue. When you define a name in the current namespace, like say when you define a function with a defunct, that's an orange in bold, and then in a module when you use a name that's defined in that very same module, that's also an orange, but a lighter shade and not in bold. When you define a local, that's in dark red, and then when you actually just use one, it's a light red, kind of a pink color. And then in some cases you have macros which introduce local names, but the name doesn't appear defined anywhere, it's just implicitly defined. In that case, it's also the same light red color, but it's bold. And then when we use a name from some other namespace, those are the names in white. And then what I call an exterior namespace is a namespace that's outside the realm of Animus itself, like say when you import packages from Java. Those are names that aren't part of Animus, so those are in that. Brownish, all of green color. And lastly, in bright yellow, a symbol with macro defined meaning is a symbol which in the context of a macro has a special meaning. It's not designating a variable. These are generally used in macros as delineators like we discussed when we talked about grouping. They usually designate a meaning for the argument which comes after them or the body which comes after them. So looking at this whole highlighting scheme in practice, here's the previous example we showed. So again, common names in blue, local names in red and module names in orange. So we're defining a module name factorial here. And I think you can very easily see which names are being defined as locals and is the parameter of the factorial function. C and T and ACC are both locals being defined and then you can see where they're being used. This is something I think which is actually really important because when you're browsing through code particularly code you're seeing for the very first time I believe it's much much easier to get a grasp on the code on just visual glance if you can quickly sort out which names belong to what namespaces. You can instantly see here which stuff is common that is stuff if you know the language you're supposed to already know and shouldn't have any trouble understanding. And then because you can see the difference between the actual and names which are part of other modules what I believe that allows you to do is first focus on the locals because these are things which you can generally understand just in the local context and then you can work outwards. You can first focus on the names and the local module before moving on to the stuff which is defined in totally different files of code. And also keep in mind all this made information that was collected one thing it allows you to do is just hover your mouse over a name and very quickly get a definition of the function from another module does. The last feature of Animus I'll discuss in any detail is what's called the language directive. A directive line must begin with an at symbol and this particular directive must always be the first line in each module. What the language directive does is simply identify which version precisely of the language is being used in this module. So here for example currently the standard directive is preliminary slash 1.0 because currently we're in a preliminary version of the language mark 1.0. The purpose of this directive the reason it's required is to allow easy transitions to future changes of the language. By explicitly declaring the precise version of the language the implementation doesn't have to guess and that at least gives implementations a fighting chance to accommodate modules written for different versions of the language. Of course going forward it's possible there might be more than just syntactical changes in a few tweaks to the standard library there might be some semantic changes in the language and in that case depending upon what that change is it may not be possible to accommodate different versions of a language or in particular to get them to interoperate. But for changes to more superficial aspects like say changes of standard library names having the language directive makes those sorts of changes pretty much pain free. I intend at some point when the implementation is stabilized a bit to make sure the code is flexible enough to allow others to easily insert their own custom dialects to the language. They designate their own language directive string. And this should allow others to both experiment and also perhaps create their own custom parsers like say for some foreign language because currently the parser I'm writing strictly restricts symbols using the English alphabet but others of course may want to create their own variant which supports non-English characters. Of course at some point I might introduce support for non-English characters in the standard variant of Animus but for now I'm just keeping things simple still I think there's value in allowing people to give their own treatment to the syntax because maybe the way the syntax rules work makes sense for English but they don't make sense to say for Japanese. So again I think it's important to allow people to at least try their hand at doing their own thing. So briefly looking at the things we didn't discuss I didn't go into any detail about how classes are implemented though I did hint that there's a class mechanism that takes after Python's support for classes. The syntax for string literals is a bit unique. It's intended to easily allow for multi-line strings. The way Java interrupt is done in Animus is a bit different from how it's done in Closure. In Closure there's some special syntax for making that work whereas in Animus it relies entirely upon just regular macros. All those special syntax rules are removed. The way we write macros in Animus is also a bit different because of how symbol resolution issues are dealt with. Animus also has a second kind of macros called prefix macros which is intended for cases where you want the invocation syntax to be as compact as possible. When it comes to invoking functions my hope is to somehow imitate in Animus the style of function invocation you find in Python where you can have parameters with default values. You have a special mechanism for expanding lists and dictionaries of arguments and you also have a facility for named arguments. I'm trying to work out some way to get to the basics of Animus. And finally when you import a module in Animus the way modules are located the way we resolved a module by its name is quite unique. I won't get into it here but it differs quite a bit from what you usually expect like say in Python. And along with this unique system of module resolution there's a concept of what are called manifest files and resource directories which again I won't get into here. Well, I hope that gives you an idea of what Animus is about. If you want to learn more go to animus.com and just remember Animus is spilt with a V instead of a U.