 Hi. So my name is Andy. I developed a programming language called Emily that I would like to show to you. Before I get started, a couple of things about this talk. First off, this is a talk about design. I'm not going to try to convince you in this talk to use the Emily programming language. That would be a different talk. This is a talk about the design of the language and my design process in making the language, which turned out to be about trying to construct a programming language from the simplest parts that I could imagine. Another thing to note, this is a side-effect positive talk. It is the opinion of me as the language designer and the opinion of the language that there is nothing weird or scary or shameful about mutable state, and this talk will not interrogate that assumption. One last thing, you will notice a carrot symbol showing up again and again in this talk, and you might find the slides a little easier to understand if you just assume that carrot means lambed Haskell users mentally substitute a backslash. In this talk, I'm going to gradually build up Emily so that you have the spoilers just so you know where this talk is going. This is a working program in Emily. You can run this on the version of Emily on the website right now, and I don't think you're going to get much out of this without me having explained the syntax or anything. But you might get a couple of things just from looking at this sample program. One thing is that this looks a lot like a Python or a Lua to my mind. It's inspired a great deal by those languages or tries to capture their feel. Another thing is you might notice, if you look carefully, that you can see signs of both functional and object-oriented programming styles on this slide. To my mind, it's like the method implementations, the insides of them are fairly functional. The overall program structure is basically object-oriented. So when I started designing a programming language, there were a number of things that I decided I didn't really like about existing programming languages, and I wished I could do something about. But I eventually decided that out of all the issues I had, there was one I was going to focus on. I was going to focus on the idea that I think there are too many redundant ideas, redundant concepts in programming languages. If you want to know what I mean by that, just look at basically anything in C++. So here I've got a slides of all the different ways in C++ that you can say, I want to get this thing out of this other thing. You've got these five different syntaxes to remember, and it's not just the fact that you're using different syntaxes. Maybe it would make sense to use different syntaxes in different contexts to help you remember what's happening. But these are actually different concepts, like six different things. You have to keep the mental baggage of remembering which of these six different things you're dealing with at any one time, and sometimes they change. I don't think this is necessary. I think that there are ways that we could take concepts in programming languages and collapse them into each other. If we find we have two concepts that partially overlap, that were partially used for the same thing, we could just collapse them into one thing, and then we could try to identify the things that used to be the two separate concepts as special cases of this one unified thing. One example of this might be an object-oriented programming. Most languages are what's called class-based, but a couple are called prototype-based, which means that normally in class-based systems, you have classes and objects, classes inherit from classes, objects instantiate classes, but you could think of instantiation as just being a funny inheritance. That means we could say, well, we don't need classes. We could just let objects inherit from other objects. At that point, a class is no longer a thing. A class is just a thing that an object is doing. There are three ideas I want to introduce, and then I'll be able to show you how Emily works, that Emily is based on. The first idea is something from the deep theory part of functional programming called logical combinators. This is a foundational model of computation. It's similar to Lambda calculus or Turing machines in that respect. What a combinator is, is a combinator is a function that takes one argument and returns one argument, and it's a higher-order function. The argument that it takes is a function, and the thing it returns is a function. It turns out this simple idea is enough to model anything that you can imagine in computation. It could emulate any programming language you can think of, you could implement any algorithm you could think of in it. For an example, here is some pseudo ML. In the expressions, we apply one combinator to one function, another by just writing one after the other. So V is a combinator that takes an argument, ignores it, and returns V. Here I'm showing that K and S at the top there, these really weird looking ML functions. S and K form a foundational basis for a combinator calculus, which means that you can take S and K, and you can do anything with just S and K. You can imagine any algorithm and you can do it just by combining S and K in different ways. Here we make V by combining this completely gibberishy string of S's and K's. One other idea, which is a little more graspable, is dictionary-based languages. This is a word I made up, but the concept is extremely common. This comes from scripting languages mostly. Python and PHP are examples of what I'd call dictionary-based languages. What I'm referring to here is the idea of a language where all data structures are built on top of a hash table. If you have any user data structure you want to make, you emulate it by emulating a stack or some other thing using the hash table type. Even more than the user data structures, the internal data structures of the language wind up being based on this dictionary type. In Python or in Lua, if you say object.child, what you're actually doing is you're looking up the string child, the key child on object. It actually de-sugars to the second thing. Even more than that, some of these languages, actually the variable scope itself is just another dictionary. If you type G by itself, you're actually looking up the string G on the scope dictionary. A third idea which follows from this one, which seems a little more unique, but I like the idea of a language where objects and functions are the same thing. Because the way I look at it, an object is just a map from field names to values. I think you can actually write out a type signature for it. If you think of the object as a map from field names to values, and if it's a map, that means it's a function as a domain and a co-domain. Here's another pseudo-MLE example. It's based on the assumption that X and Z are values. In something like ML with records or something like C++ with structs, the field names aren't things. They don't exist by themselves. They're just syntax. But in some languages, like the dictionary-based ones I mentioned, the field names are things. You can store them in variables, and that means this is actually a sensible thing to say. So armed with these three ideas, I am going to construct an entire programming language in the next 10 minutes. This language is going to be based on combinators. What combinators are going to do for me is they are going to provide the structure of the language. All I really mean by this is that everything in the language is a function that takes one thing as an argument and returns one thing as a result. This is actually not that weird. I'm calling this, are we able to see expressions? I think of this kind of expression here with like just a series of tokens written one after the other as this C expression or curried expression. I'm saying that because to me, it's analogous to the S expression and Lisp. In Lisp, you have this S expression idea, which means that your code is a data structure. It's a series of nested lists. This is actually the same thing. It's just that it's for a curried function where the Lisp X expressions are for functions with argument lists. All that happens here is when you want to evaluate this, you take A and you apply it to B, this returns a new function X, you apply X to C, that returns Y, which you apply to D, apply to E. This actually isn't weird. This isn't something I came up with. This is actually just how expressions work in ML or Haskell. You just write a series of data items one after the other. Some of them look like argument lists, but they're just tuples. A tuple is a singular item, that sort of thing. Like I said, you can think of this as a data structure. You can think of this as a tree of applications. What are we going to put in these C expressions? Well, here's just some basic things you might want in a programming language. You want numbers, you want strings, you want lambdas, you want anonymous functions obviously, and atoms. Atoms are a slightly weirder thing. They're what common Lisp people might call symbols. The idea is that an atom is actually just a string, but the atom A is not equal to the string A. I'm going to also say that the compiler can somehow figure out what all of the atoms in the entire program are going to be, because they're all written out in this particular way, like dot atom. That means you can potentially intern those strings, which lets you treat them more efficiently. You might note at this point, well, wait, wait, she said combinators are going to be everything, but these are not combinators. I've got a trick coming up. Before I get there, so this language is going to have objects, but like I said, objects are just functions in my mind. They're mappings. And so object here maps the value dot x, the atom dot x, to the number three. And how did it get there? Well, it got there because the object has a member named dot set. And dot set sets a binding on object. It changes the value in binding. It alters the mapping. And it's doing that just with functions. Objects have prototypes. They have inheritance. If you look up the dot, the atom x mapping on object, and object doesn't have an x field, it will automatically check. It will look up the dot parent field automatically, and it will forward to that. You could have a chain of these. So we've just implemented inheritance by just saying forward to this parent. The built-in types, things like three, are objects. And their parents are these kind of core prototypes. Somewhere there's a number prototype that has methods like dot plus on it. So that means if I say three dot plus, well, three is an object, which means it's a function, which means that it can take the argument dot plus and map that to a new function, which adds three to whatever you feed to it. And again, these are still just functions. So you could take that three dot plus, and instead of feeding it a number, you could just store it somewhere. You could think of this as currying on the three function. I'm gonna go a little bit further than that. So I'm gonna find these objects. I'm gonna say like in Lua or in some of those other languages, the scope, the scope that you pull variables from is also an object. So if you type out variable, you're actually just looking up the variable atom on the current scope function. So at this point, I can make expressions that are relatively complicated. That's not enough to make a program. I'm going to introduce one more idea, which is the idea that expressions can be sequenced. I'm just gonna say semicolon means you evaluate this and then evaluate this. Because this language has side effects, it could make sense to have a value whose result is ignored. And we're going to do that with semicolon. So whenever you have a parenthesis group, if it has multiple semicolons within it, the final item is the thing that the entire parenthesis group returns. And because I don't like symbols, I'm going to say that the new line is also equivalent to the semicolon. Curly braces do something a little more interesting. So the parentheses form an expression inside of themselves and the parentheses can allow you to have these sequence statements, some of which are ignored. The curly braces, the exact same as the parentheses, but within the curly brace, you're using a new scope. And what I'm actually going to say happens here is that when you get to the left curly brace, it creates a new scope object. Sets that scope object's parent to the enclosing scope and then you're using that scope for the remainder of the closer. So I've just implemented scope hierarchy with object inheritance, which is kind of amusing to me. One more slightly weirder thing is square brackets are also going to be like parentheses. And the way this is going to work is it's going to be like the curly brace and that when you open the square bracket, it's going to create an object, set it as the scope, but then at the end of the square bracket expression, instead of returning the last value, we're going to return the scope that we created. So the idea is that we have this scope inside of the square bracket. We're calling set on it over and over. I accidentally said let instead of set, sorry, on this slide. You're calling set over and over inside of the square bracket scope and what you're doing is you're adding mappings to this scope object, which means you're actually adding mappings to the value you're going to have at the end. So we can use this to make something like an object literal. Have I lost anyone yet? Okay. So we now have enough to actually write a program with and this is a pretty simple thing. It's an absolute value function and I'm doing a couple of things I haven't explained here yet. One is that you'll notice there's a couple of anonymous functions here at the bottom that don't have arguments listed. I'm just going to say if you don't list an argument, then it still takes an argument because everything in this function is, everything in this language is formally a function that takes an argument and returns an argument but in this case it just takes an argument and throws it away because there's nothing useful in this particular situation. There's also this function named turn for ternary operator. This is how you implement if in this language. The idea is that turn takes three arguments. It evaluates the first. If it's true, it calls the second, the function that's argument two. If it's false, it calls the function that's argument three. So based on this we can just say make a anonymous function which evaluates its argument. If it's greater than zero returns the argument. If it's less than zero, it returns the negation of the argument and then it takes this anonymous function and it sets it, it binds it to the variable name abs. So this is actually kind of cool, I think. This is like this sort of slightly illispy thing because it's all functions. Actually it's slightly more function-based than list because we're doing it without any special forms. There's no let here. Everything is a function including the way we bind objects. This is really unreadable. Like just incredibly unreadable. So let's see what we can do about that. I'm going to attack the unreadability with macros which means I'm going to say by convention in this language any symbol is not part of the language. It's a macro that's going to be kind of rewritten into other code before the thing is evaluated. So for example, if I say E equals B that will when the code is run be treated as set dot A B and that allows me to avoid the slightly weird looking set construction makes something that's a little more familiar. So I'm going to introduce a bunch of macros at once. I have A equals B for setting the variable A. I have A dot B equals C for I have an existing object A. I'm going to set the B field on it. So I'm going to call set on A instead of the scope object. And then I've got arithmetic operators and the arithmetic operators just call dot plus or dot times or whatever on the first argument but they do something a little tricky. They wrap the space before and the space after them in parenthesis and what that's going to do for you is it's going to allow me to kind of if the macros are evaluated in a particular order it allows me to recreate the idea of operator precedence because in this last little bit down here A plus B times C by the time plus is evaluated in a new expression it's already been quarantined in parentheses by the star macro. Couple slightly more weirder things. I'm going to use tilde A for negation because ML. I'm going to say colon is just the dollar sign from Haskell. I wrap the right side of A in parentheses because I hate symbols and I want to avoid them wherever I can. I'm going to have the question mark colon operator from C the ternary operator. All it does is call turn. It's not doing anything magic. And then because you spend a lot of time binding variables to anonymous functions in this language I'm going to make a sugar for that where if you just put the carat on the left it automatically moves it to the right for you. So if we have all these macros we can take this really ugly absolute value function and we can turn it in this thing up top that well if you're a C programmer you'll find that attractive but I am. So in fact we now have enough that we've now explained just in the last, did I make it? 10 minutes, okay. Enough that that entire program from the first slide we can run this now and what we do here is we define our little absolute value function. We define a square root function using a recursive approximation algorithm. Armed with these two things we create a point class which has various functions that you might want on a point object like plus and minus which remember if we use the plus sign or the minus sign it'll actually be calling these plus and minus functions. And then we have a new method which can be used to construct a new point by which we mean an object which has point as a parent. And at the bottom we just say point dot new three five plus point dot new minus four two. Asks for the length and it prints out the length of whatever that vector that we just made is. So what have we just done here? Well for one thing I think that I've actually accomplished my little ideological goal of collapsing things into other things. So whereas Lua objects manage to take objects and classes and collapse them with this idea of a dictionary I've done that and have also collapsed them with the idea of functions which is ideologically kind of neat. Practically the fact that everything works this way the fact that everything in this language that is a function application there is no additional syntax unless you count the anonymous function creator and I think I can get around that with weird combinator magic. You can, the idea is that all of the things this language does are based on this simple idea of composing functions. There's nothing magic the language implementation is doing which means that you could add new abstractions to the language really easily that are just as powerful as the ones I as the language creator built in. So there's an example here with I've got some code on this slide where you have an object which inherits from a function because why not? And I'm using this actually to implement dual inheritance this two line function. There's no dual inheritance in Emily by default. You can add dual inheritance with this two line function it would be one line if the screen were wider. Another thing that's kind of cool about this is that there are no different syntaxes in different context you don't have to remember where you are when you write a block of code unless you count the left side of the equals which is a little weird. But like for example, inside of the object literal brackets that's just code you're just running functions and you can do really weird stuff like have some kind of a function that prepares a scope in a particular way and then call it on the scope while you're inside of the object bracket which is maybe not exciting to admit me. There's some other quirkier advantages that you get out of this everything is one thing sort of thing. I claim that once it's all done I'm going to be able to make Emily better at language interoperability than anything else out there. And the reason why is that whatever the abstractions of the concepts are in this other language that you want to FFI with I can represent it within Emily's syntax. So like for an example here I've imagined I've got a C plus plus class and then down here I've got like some kind of wrapper function for the class that can create a wrapper function for an object and I'm just calling the methods or whatever if this were a different kind of language that allowed different kinds of things I could still just write out whatever it is I needed and it could be translated to what that other language's object wrapper wants and once I have a type system in I could even set it up where whoever wrote the C plus plus FFI library could restrict you to whatever it is that that language actually supports as operations on its primitives. So I just mentioned types and I don't have any background in types. I've done some of the stuff before everything I've shown you up to this point is stuff that actually works in the working interpreter right now. Types, everything I'm about to say is speculative but the way I want to, I do want types in this language and the way I want to do this is so far I've said that I have this one thing, combinators and the combinators I've used them to represent objects, classes, dictionaries, functions, scopes, there was something else, oh yeah arrays and I'm going to say I can also use this single combinator idea to represent types. In this language what I want to do is I want to say a type is just a predicate function that accepts or rejects something as being a member of a particular class and I'm going to have some kind of an operator like is where I say for is int, int is just a function that returns true if it's an integer or false if it's something else and when you call it is well there's no syntax in Emily there's just object applications so is is actually going to be a macro where you do something like test if x is an int if it is return x otherwise halt the program and but because it's a function you wouldn't have to be limited to these built in things like int you could just make some random thing up like if it responds well to greater than three say it's in this new type class so you can have a type for greater than three pretty trivially. This isn't sound but you could imagine there being a subset of it which is sound and there's a couple things I can do once I've got this in the language I think that I could have a compiler that's smart enough for that subset of things which is sound and is possible to check I could take all those is's and I could just optimize them out at compile time if I can prove that something's an int I don't need to do the is int check again similarly I could maybe do some smart and ferrant stuff if you imagine these two lines are an entire program well this is an object and in order for this to execute in Emily it has to do some really inefficient stuff I have to locate this object and then I have to call the hash function set thing twice for dot x and dot y but all I'm actually doing in this entire program is just putting two things into x and y and then pulling them out so the compiler could be smart enough to say I could represent this object as just a block of memory and I could be the sets or the reads on x and y could just be reading and writing memory and maybe at some point escapes this function it gets called somewhere else but when it escapes the thing that the type checker knows it's safe to just treat write x read y as memory accesses maybe it gets closed in a wrapper function at that point I can also do this thing I'm incredibly amused by which is I could imagine if we imagine that the the objects are actually just functions we could imagine something like you know the function bar bar bar thing in ML this might not be entirely clear but what I'm imagining is within the square brackets all I've got is an object literal here but I could say that fields on this object literal have as keys types or something so like the first value here is for the key zero return zero for a key where x is something which is greater than zero return the string positive for x which is less than zero return negative otherwise return not a number and I've got this switch statement which actually literally just switches the argument I've just recreated pattern matching using only the stuff that I introduced earlier because this switch thing is going to treat this object literal as a function pass y into it and then that will evaluate to one of these cases that are just sort of looking up as if their fields on this object I think I can do this another slightly we're gonna think I can do is if at some point types can be used to put restrictions on things like saying you can't read or write this Emily is fairly extensible the macros can all be replaced and the things like curly bracket and square bracket eventually I want those to be actually just macros so you could imagine having some sort of a way you could just say backslash load purity and all of a sudden the scope objects are all immutable and maybe that plus a couple other things would allow you to basically create a pure subset of Emily that could still interact with the other parts of Emily and have all the purity guarantees enforced by the type system I hope so that's all I've got if you want to know more you can download all this and see the documentation at the website and you should follow Emily language the language Twitter what you should not do is follow my personal account because it consists entirely of cryptic sentence fragments and gay politics so that's all I think I've got a couple of minutes left the last one ran a little bit long well I don't know if I have I had exactly 24 minutes and 24 seconds does anyone have any questions?