Amsterdam.rb


This Monday I took a trip to Amsterdam to attend and present at Amsterdam.rb. TTY, the company hosting this months meeting, had managed to get a pretty interesting lineup, and about 70 people turned up (in fact, that was the limit, so it might be possible to get more people).

This was my first time in Amsterdam, but due to me being really, really sick, I didn’t get to see much of the city. I did manage to spend a few hours with Sam Aaron and his Haskell friends, discussing programming languages and Ioke specifically. Much fun.

There were six sessions planned, but since the schedule was busted from the beginning, the TTY talk on quality had to be removed (which is a shame – I was interested in what it would be about).

The first presentation was a discussion about a tool that could convert UML and YAML description files into scaffolding with internal associations. The main goal for the tool seemed to be to really rapidly get an application started, but the workflow goes totally against the way I generally write Rails apps, so I had a hard time seeing any utility for myself.

After that, Martijn van Excel gave a presentation about OpenStreetMap. I found this extremely interesting. The project seems to be the wikipedia equivalent of maps, which means that areas that Google aren’t interested in mapping will still get mapped in OSM. An example Martijn displayed was a large city in the Philippines, where Google really didn’t have much information, while OSM was extremely detailed. After some discussions and questions, it does seem that OSM has the same drawbacks as wikipedia – spurious updates, trust issues and so on, but I didn’t get the feeling that OSM had implemented the same kind of verification structures that wikipedia uses – at least not yet. All in all a cool project, though. Will definitely consider it if I ever need to do map-based mashups. The lack of geocoding might be a problem, of course.

After Martijns presentation there was a break, and then it was my turn to talk about Ioke. All in all, I spent way to much time explaining some intricacies of the syntax, which meant I didn’t have enough time to talk about the really important stuff – things like macros and the testing and documentation tools. I need to reconsider how to present the material without getting bogged down with details that doesn’t matter as much. All in all I’m still happy about it, though. You can download my slides at: http://ioke.org/presentations/Amsterdam.rb.pdf.

After my talk, Ninh Bui and Hongli Lai from Phusion talked about how they do design by contract. It was a very polished presentation, and I liked the way they did it – but I still felt that I didn’t grasp exactly how they applied DbC. I would have liked to have a long discussion with them about it, since the presentation gave the impression that the main way they did DbC was by duplicating the contracts in comments, in code and in tests. Interesting discussion on how DbC interacts with LSP (Liskov’s Substitution Principle). Once I realized it was isomorphic to the way bounded generics work in parametric polymorphism, it became much simpler to get my head around.

The final talk was about RubyCocoa. At that point my fever had started to fry my brain, so I didn’t really get much out of that talk.

All in all, a great time. Met many interesting people and had some good discussions. The Amsterdam hackers are obviously very cool and I hope I can get back there sooner or later.

There is some photos from the event here: http://amsterdamrbfebruari09hostedbytty.shutterfly.com/.



Ioke at Amsterdam.rb


Monday evening I’ll be in Amsterdam, talking about Ioke to Amsterdam.rb, inbetween some other presentations. Seeing as this is going to be the first public presentation about Ioke, I’m absolutely thrilled. It’s going to be great fun. I still haven’t decided exactly how to present Ioke, but I think it’s going to be eased by this being a Ruby crowd.

As far as I understand, the evening is actually fully booked, so there is no more space for people to come. But hopefully everyone in Amsterdam who is interested in Ioke (all three of you? =) will be there. The presentation will be at TTY’s offices in Amsterdam, from 19. It seems my presentation will be from 20:35.

Hope to see you there. This might be an historic event! =)



Ioke is not a Lisp


I generally get lots of different kinds of reactions from people when showing them Ioke. A very common misconception about the language is that it is “just another dialect of Lisp”. That is a quite easy mistake to make, since I’ve borrowed heavily from the things I like about Lisp. But there are also several things that make Ioke fundamentally different. Ioke is Lisp turned inside out and skewed a bit.

First we have the syntax. Ioke has syntax inspired by Ruby and Self, but includes some variations that come from Common Lisp too. The main difference between how Ruby and Ioke handles syntax is that Ioke will transform everything down to the same message passing idiom. Most of the syntax is in the form of operators, which don’t get any special handling by the parser at all. But the syntax is still there, and it is also deeper embedded compared to how Lisp generally does it. Ioke acknowledges the existence of different kind of literals – and allow you to override and handle them differently if you want. One of the examples in the distribution is a small parser combinator library. It allows you to use regular text and number literals, but in the context of the parser those literals will return new parser for their type.

Common Lisp can play these syntactic games with reader macros, but it is generally not done. Ioke embraces the use of syntax to improve readability and the creation of nice DSLs.

Of course, any Lisp guy will tell you that syntax has been tried – but programmers preferred S-expressions. The latest example of this is Dylan. But I’ll have to admit that if you look at the Dylan syntax, you understand why the programmers didn’t feel like bothering with it. It’s one thing to try syntax by just adding some very clumsy Algol-derivation. It is another thing entirely to actually focus on syntax.

That said, Ioke is homoiconic, and translates to a structure that could easily be represented as cons cells. It doesn’t, though, since the message chain abstraction works better.

The other thing that really makes Ioke different from Lisp – and also a reason that would make infix S-expressions extremely impractical – is that Ioke is fundamentally object oriented based on a message passing idiom. In a lisp, all function calls are evaluated in the context of the current package (in Common Lisp). You can get different behavior if the function you call is in fact a generic method, but in reality you’re still talking about one method. If you want to chain method calls together, you have to turn them inside out. That doesn’t lend itself well to a message passing paradigm where there is an explicit receiver for everything.

Ioke in contrast have the message passing model at its core. Any evaluation in Ioke is message send to some receiver. In that model, you really need to make it easy to chain messages in some way. And that’s why S-expressions would never work well for Ioke. S-expressions fundamentally doesn’t use the concept of a receiver at all.

All the other differences in Ioke versus any Lisp could be chalked down to minor dialectical differences, but the two biggies are the above. Ioke is not a Lisp. It’s heavily inspired by Lisp, but it’s fundamentally different from it.



Ioke’s need for speed


I find it interesting that one of the expectations on Ioke is that it will be faster than Io. I need to make this really, obviously clear. Ioke is not even close to the speed of Io. Ruby is a blazing arrow compared to Ioke. I know this, and I’ve made a clear design decision to not take performance into consideration at all right now.

There are several ways to think about this. I’m creating Ioke for several reason. One of them is to see how far you can push expressability in a language. Can you take the blub hierarchy another level? To be able to do this, it’s impossible to be concerned with speed.

I realize that if Ioke someday becomes a successful language, performance will be more of an issue. But there are several implicit assumptions in that statement. The first one is that it will be successful at all. After all, most languages fail. If Ioke fails to become successful enough (for any definition of the term), it will almost certainly not be because of lack of speed. And if lack of speed is the only problem, well, that can be fixed by focusing totally on performance after the fact. But I feel that it is much more important to first get the model correct, and then optimize, rather than the other way around.

Programmers all know the old aphorism about the dangers of premature optimization. But we still do it. All the time. It is really hard for me to try to avoid it in Ioke. But I think I’m succeeding, because Ioke is really dead slow right now.

Ioke does need speed. But it doesn’t speed right now. There are many ifs in the success of a language, but if all of them are fulfilled – well, then maybe in a year or two a focus on speed will come. Ioke is a malleable language, and it will be possible to do all kinds of tricks with the language to make performance improvement happen. But that is a consequence of the runtime model. The fact that the language is really flexible makes it slow now, but will make it easier to improve performance on later.



A folding language


In the descriptions of Ioke, I’ve lately favored using the concept of a “folding language”. I think that description feels very right, and I’ll explain a bit more about that here. I didn’t come up with the term though – Steve Yegge did, in a passing remark in his post “Wizard School”. It’s a funny read, actually. When I read it, the remark about folding languages stayed with me, and for me, Ioke is exactly that.

Let me first reproduce the paragraph from Yegge’s post that mentions this concept:

Of course, blasting out hundreds of thousands of lines a year is missing the point. If a Wizard is given complete technical control over a project (which is usually the smartest thing to do, but companies are rarely very smart), the Wizard will typically write in one of the super-succinct “folding languages” they’ve developed on campus, usually a Lisp or Haskell derivative.

They call them Folding Languages because they write code that writes code that writes code… Wizards swear by it, and there’s no question that they can produce amazingly compact, fast, clean-looking code. But 90% of the devs out there claim they can’t read it, and whine a lot about it to their bosses. Given that most companies can only afford a few Staff Wizards, the Wizards are usually forced to capitulate and use Java or C++. They’re equally comfortable in any language you throw at them, though, and if you force them to use a verbose language, well, you get what you ask for. It still amazes me that companies are bragging about how many lines of code their Wizards have produced. Potential startups take note: your competitors are usually idiots.

Especially in the evolution from Ioke 0 to Ioke S – when I added syntactic macros – the ways I can write code manipulating code is really nice. I am used to it from Lisp, of course, but to me Ioke have this in spades. It feels like a Lisp turned inside out.

Ioke is designed to be expressive, and being able to fold code is an important part of this. Earlier today, Sam Aaron talked about folding languages on IRC, and he said this (I have added some punctuation, that doesn’t change the meaning of the quote):

When I read it I first thought that all languages support folding to some extent, but some languages have limitations – such as the limitations of paper, i.e. with paper you can only fold it so many times before it’s impossible to add another fold, whereas languages like Ioke seem to support infinite folding. And some languages are just so brittle, that folding is pretty impossible.

That pretty much says it all. =) If you want a good example of how Ioke supports code folding, go to the git repository and take a look at the evolution of the Enumerable mixin from Ioke 0 to Ioke S. Let me take as an example the implemention of any?. (I will provide the real source, except for the documentation text). In Ioke any? can take zero, one or two arguments. If you give zero arguments, the method will return true if there are any true values in the collection. If you give one argument, that argument is a message chain that will be applied to every element in the collection. If that message chain returns true for any element, then the any?-method returns true. Finally, the two argument version takes one variable name, and one piece of code that uses that variable name. The code will be executed once for every element with the current element bound to the name. Some usage examples:

[nil, false, "foo"] any? ; => true
[1, 2, 3, 4] any?(odd?)  ; => true
[1, 2, 3, 4] any?(x, x*x > 10)   ; => true

As you can see, these versions all make sense. And what is more, this pattern of taking zero, one or two arguments is pretty common in the Enumerable implementations. Most of the implementations are actually variations on this theme.

So, the implementation of any? in Ioke 0 looked like this:

Mixins Enumerable any? = macro(
  len = call arguments length
  if(len == 0,
    self each(n, if(cell(:n), return(true))),

    if(len == 1,
      theCode = call arguments first
      self each(n, if(theCode evaluateOn(call ground, cell(:n)), return(true))),

      lexicalCode = LexicalBlock createFrom(call arguments, call ground)
      self each(n, if(lexicalCode call(cell(:n)), return(true)))))
  false)

Pretty interesting code, right? Well, there are several problems with it. First, the actual logic of what it does is spread out in three different places, one for each variation in arguments. The second problem is that this code doesn’t check the argument arity. If you give another combination of arguments, it will fail noisily. But the most important problem is that this structure is the same for most of the Enumerable methods. Take a look at two more, none? and find.

Mixins Enumerable none? = macro(
  len = call arguments length
  if(len == 0,
    self each(n, if(cell(:n), return(false))),

    if(len == 1,
      theCode = call arguments first
      self each(n, if(theCode evaluateOn(call ground, cell(:n)), return(false))),

      lexicalCode = LexicalBlock createFrom(call arguments, call ground)
      self each(n, if(lexicalCode call(cell(:n)), return(false)))))
  true)

Mixins Enumerable find = macro(
  len = call arguments length
  if(len == 0,
    self each(n, if(cell(:n), return(it))),

    if(len == 1,
      theCode = call arguments first
      self each(n, if(theCode evaluateOn(call ground, cell(:n)), return(cell(:n)))),

      lexicalCode = LexicalBlock createFrom(call arguments, call ground)
      self each(n, if(lexicalCode call(cell(:n)), return(cell(:n))))))
  nil)

Can you see the difference and describe what they do? I can definitely say that for me, maintaining these methods got problematic quickly. And as you see, the structure is exactly the same, but since the insides of it are quite different, it would be hard to do something about this code in Java, except refactor out small methods for different things. But the essential duplication would still be there.

In Ioke S, the implementation of any? looks like this:

Mixins Enumerable any? = enumerableDefaultMethod(
  .,
  if(cell(:x),
    return(true)),
  false)

enumerableDefaultMethod takes three arguments of code. The first argument is what should be done before (nothing in this case, hence the dot). The second argument is the code that should be executed on each iteration. And the final argument is the code that should be used to return something after the iteration. The n cell is the same as in the long implementations – the current value in the collection. The cell x is the value after applying the code to it. With this understanding in place, you can see that any? will go through each element, check if the transformed value is true, and in that case return true. If none is true, it returns false. Notice that enumerableDefaultMethod is not an exposed method. It’s not part of any API – it’s just a syntactic macro used to make it really easy to implement these Enumerable-methods.

It also gives some other things. For example, it will automatically add arity-checking, so that this method will now return a meaningful condition if given the wrong kind of arguments.

And lets go to the Ioke S implementations of none? and find:

Mixins Enumerable none? = enumerableDefaultMethod(
  .,
  if(cell(:x),
    return(false)),
  true)

Mixins Enumerable find = enumerableDefaultMethod(
  .,
  if(cell(:x),
    return(cell(:n))),
  nil)

Here you clearly see the difference between any? and none?. And it is also clear what find does too, namely if any transformed value is true, return the original value for that transformation. Otherwise return nil.

Now, enumerableDefaultMethod is actually implemented using dsyntax. And dsyntax is a syntax that returns a new syntax, that will finally execute and expand the implementations. And that is what I mean with code folding. You can take coded abstractions and fold the code until the only thing you need to write is what makes this different from the defaults. The above code could probably have been even easier to read by assuming that an omitted first argument means there will be no initialization. So as you can see, this process never really stops. But it allows you to code on a higher level. And when you find the right abstractions, what you end up writing is much closer to the business domain. And that is really what most developers want, right?



Ioke S released


Exactly one month after the first release of Ioke, I am very happy to announce that Ioke S has been released. It has been a team effort and I am immensely pleased with the result of it.

Ioke is a language that is designed to be as expressive as possible. It is a dynamic language targeted at the Java Virtual Machine. It’s been designed from scratch to be a highly flexible general purpose language. It is a prototype-based programming language that is inspired by Io, Smalltalk, Lisp and Ruby.

Homepage: http://ioke.org
Download: http://ioke.org/download.html
Programming guide: http://ioke.org/guide.html

Ioke S is the second release of Ioke. It includes a large amount of new features compared to Ioke 0. Among the most important are syntactic macros, full regular expression support, for comprehensions, aspects, cond and case, destructuring macros, and many more things.

Ioke S also includes a large amount of bug fixes, and several example programs.

Features:

  • Expressiveness first
  • Strong, dynamic typing
  • Prototype based object orientation
  • Homoiconic language
  • Simple syntax
  • Powerful macro facilities
  • Condition system
  • Aspects
  • Developed using TDD
  • Documentation system that combines documentation with specs
  • Wedded to the JVM

The many things added in Ioke S could not have been done without the support of several new contributors. I would like to call out and thank:
T W <twellman@gmail.com>
Sam Aaron <samaaron@gmail.com>
Carlos Villela <cv@lixo.org>
Brian Guthrie <btguthrie@gmail.com>
Martin Elwin <elvvin@gmail.com>
Felipe Rodrigues de Almeida <felipero@gmail.com>



Guide to Ioke development environment


Wonderful! Martin Elwin posted the first part of a series on setting up an Ioke development environment. It’s fantastic to see people blog about Ioke, and Martin wrote some other pieces too. Especially the JSON parser is cool. You can see the development environment post here.



Talking about Ioke, at Amsterdam.rb


Thanks to Sam Aaron, I will talk about Ioke at Amsterdam.rb February the 23rd. Great stuff. Not sure what I will cover until then, since it’s so far in the future.

For the record, since I released Ioke 0 on December 23rd, it would be fun to release Ioke S on Jan 23rd, and Ioke P on Feb 23rd. The Ioke S release date I think I can hold, but the P one is much more worrisome. Anyway, see you in Amsterdam, hopefully!



Macro types in Ioke – or: what is a dmacro?


With the release of Ioke 0, things regarding types of code were pretty simple. At that point Ioke had DefaultMethod, LexicalBlock and DefaultMacro. (That’s not counting the raw message chains of course). But since then I’ve seen fit to add several new types of macros to Ioke. All of these have their reason for existing, and I thought I would try to explain those reasons a bit here.

But first I need to explain what DefaultMacro is. Generally speaking, when you send the message “macro” in Ioke, you will get back an instance of DefaultMacro. A DefaultMacro is executed at runtime, just like regular methods, and in the same namespace. So a macro has a receiver, just as a method. In fact, the main difference between macros and methods are that you can’t define arguments for a macro. And when a message activates a macro, the arguments sent to that message will not be evaluated. Instead, the macro gets access to a cell called “call”. This cell is a mimic of the kind Call.

What can you do with a Call then? Well, you can get access to the unevaluated arguments. The easiest way to do this is by doing “call arguments”. That returns a list of messages. A Call also contains the message sent to activate it. This can be accessed with “call message”. Call contains a reference to the ground in which the message was sent. This is accessed with “call ground”, and is necessary to be able to evaluate arguments correctly. Finally, there are some convenience methods that allow the macro to evaluate arguments. Doing “call argAt(2)” will evaluate the third argument and return it. This is a short form for the equivalent “call arguments[2] evaluateOn(call ground, call ground)”.

This is all well and good. Macros allow you to do most things you would want to do, really. But they are quite rough to work with in their raw form. There are also plumbing that is a bit inconvenient. One common thing that you might want to do is to transform the argument messages without evaluating them, return those messages and have them be inserted instead of the current macro. You can do this directly, but it is as mentioned above a bit inconvenient. So I added DefaultSyntax. You define a DefaultSyntax with a message called “syntax”. The first time a syntax is activated, it will run, take the result of itself and replace itself with that result, and then execute that result. The next time that piece of code is found, the syntax will not execute, instead the result of the first invocation will be there. This is the feature that lies behind for comprehensions. To make this a bit more concrete, lets create a very simplified version of it. This version is fixed to take three arguments, an argument name, an enumerable to iterate over, and an expression for how to map the output value. Basically, a different way of calling “map”. A case like this is good, because we have all the information necessary to transform it, instead of evaluating it directly.

An example use case could look like this:

myfor(x, 1..5, x*2) ; returns [2,4,6,8,10]

Here myfor will return the code to double the the elements in the range, and then execute that.

The syntax definition to make this possible looks like this:

myfor = syntax(
  "takes a name, an enumerable, and a transforming expression
and returns the result of transforming each entry in the
expression, with the current value of the enumerable
bound to the name given as the first argument",

  argName = call arguments[0]
  enumerable = call arguments[1]
  argCode = call arguments[2]
  ''(`enumerable map(`argName, `argCode))
)

As you can see, I’ve provided a documentation text. This is available at runtime.

Syntactic macros also have access to “call”, just like regular macros. Here we use it to assign three variables. These variables get the messages, not the result of those things. Finally, a metaquote is used. A metaquote takes its content and returns the message chain inside of it, except that anywhere a ` is encountered, the message at that point will be evaluated and spliced into the message chain at that point. The result will be to transform “myfor(x, 1..5, x*2)” into “1..5 map(x, x*2)”.

As might be visible, the handling of arguments is kinda impractical here. There are two problems with it, really. First, it’s really verbose. Second, it doesn’t check for too many or too few arguments. Doing these things would complicate the code, at the expense of readability. And regular macros have exactly the same problem. That’s why I implemented the d-family of destructuring macros. The current versions of this are dmacro, dsyntax, dlecro and dlecrox. They all work the same, except the generate macros, syntax, lecros or lecroxes, depending on which version used.

Let’s take the previous example and show how it would look like with dsyntax:

myfor = dsyntax(
  "takes a name, an enumerable, and a transforming expression
and returns the result of transforming each entry in the
expression, with the current value of the enumerable
bound to the name given as the first argument",

  [argName, enumerable, argCode]

  ''(`enumerable map(`argName, `argCode))
)

The only difference here is that we use dsyntax instead of syntax. The usage of “call arguments[n]” is gone, and is instead replaced with a list of names. Under the covers, dsyntax will make sure the right number of arguments are sent and an error message provided otherwise. After it has ensured the right number of arguments, it will also assign the names in the list to their corresponding argument. This process is highly flexible and you can choose to evaluate some messages and some not. You can also collect messages into a list of messages.

But the real nice thing with dsyntax is that it allows several choices of argument lists. Say we wanted to provide the option of giving either 3 or 4 arguments, where the expansion looks the same for 3 arguments, but if 4 arguments are provided, the third one will be interpreted as a condition. In other words, to be able to do this:

myfor(x, 1..5, x*2) ; returns [2,4,6,8,10]
myfor(x, 1..5, x<4, x*2) ; returns [2,4,6]

Here a condition is used in the comprehension to filter out some elements. Just as with the original, this code transforms into an obvious application of “filter” followed by “map”. The updated version of the syntax looks like this:

myfor = dsyntax(
  "takes a name, an enumerable, and a transforming expression
and returns the result of transforming each entry in the
expression, with the current value of the enumerable
bound to the name given as the first argument",

  [argName, enumerable, argCode]

  ''(`enumerable map(`argName, `argCode)),

  [argName, enumerable, condition, argCode]

  ''(`enumerable filter(`argName, `condition) map(`argName, `argCode))
)

The only thing added is a new destructuring pattern that matches the new case and in that situation returns code that includes a call to filter.

The destructuring macros have more features than these, but this is the skinny on why they are useful. In fact, I’ve used a combination of syntax and dmacro to remove a lot of repetition from the Enumerable core methods, for example. Things like this make it possible to provide abstractions where you only need to specify what’s necessary, and nothing more.

And remember, the destructuring I’ve shown with dsyntax can be done exactly the same for macros and lecros. Regular methods doesn’t need it that much, since the rules for DefaultMethod arguments are so flexible anyway. But for macros this has really made a large difference.



Operators in Ioke


When I first published the guide for Ioke, one of the reactions was that that was one heck of a lot of operators. The reason being that I listed all the available Ioke operators in the guide, and there are quite a lot of them. What is implicit in the reaction is that these operators all have defined meanings and are used in Ioke. That’s not true at all. So, I thought I’d describe a little bit more what operators are, how they work, and why they are necessary in a language like Ioke.

First of all, all available operators can be found in the Ioke grammar file. They are available as the tokens ComparisonOperator, RegularBinaryOperator and IncDec. Together, they are at the moment 10 + 77 + 2 = 89 operators. Together with assignment, that is 90 available operators. As mentioned above, most of these aren’t actually used anywhere. Instead they are available for use by any client program. They are there specifically for creating nice DSLs and readable APIs.

Operators only exist in the parsing stage of Ioke. After that everything is a message, and a message based on an operator is no different from one based on a regular message send. So the importance of operators happen mostly in the stage of operator shuffling. Since Ioke uses whitespace for regular message application, the implementation could treat EVERYTHING the same. That is also the base case. If you turn of operator shuffling, you could write =(foo, 1 +(2 *(20 **(42)))) to assign an expression to the name foo. This way of writing isn’t necessarily convenient, though, which is why Ioke adopts an operator shuffling scheme similar to Io.

So, what is an operator? It is really just a method with some funky characters in the name. All operators in Ioke are binary or trinary. What looks like a unary operator is simply just a message send to an implicit receiver. So 10 – 5 is the same operator as -5. It’s just that the second version will call the method – on DefaultBehavior, giving it 5 as argument. The result will be the negation of the argument. Binary operators aren’t anything strange, but when I say trinary operators, people will probably think about the ?: combination available in some languages. That isn’t exactly what I mean. There is another distinction between operators that is useful in Ioke – that between assigning operators and regular ones. The assigning operators are =, things like +=, /= and the increment and decrement operators ++ and –. All assigning operators are trinary except for the increment and decrement operators.

So what does this mean? (And I realize this is becoming a bit rambling at this point…). Ok, the rule is this. All assigning operators takes a place to assign to. That is the left hand side. Everything except for increment and decrement takes a value to send to the assign operator. But that leaves the actual receiver of the assignment message. Since assignment is just a message like everything else, there must be a receiver. So, in Ioke, if I write  foo += 1+2, that will be translated (I will explain this later), into  +=(foo, 1 +(2)). At this stage it looks like the += message is sent without receiver, but everything in Ioke has a default receiver – called the ground. In another situation, suppose we have a class like object called Foo. Then the expression  Foo bar = 42   will be translated into  Foo =(bar, 42). Here it is more apparent that the receiver of the =-message is actually Foo, and the direct left hand side and right hand side of the =-sign are both arguments. This means that there are three operands in these assignment operators and that is why they are called trinary.

Back to operator shuffling. In the examples I’ve shown above, the operator shuffling step is code that will basically take something that likes like regular arithmetic or assignment and rearrange that into the real message form. So x+1 will be translated into   x +(1). Something like   2+2*3 will be translated into 2 +(2 *(3)). And all operators translated this way has an associativity to make sure they follow expectations. You tune this associativity using the Dict inside Message OperatorTable operators. This can be used to create DSLs with new or different operators.

One thing that might surprise some people is that regular alphabetical names can be used as operators to. That is how something like “and” can be used in infix position.

I know that the operator rules are a bit complicated, and the grammar isn’t fantastic either. But when working with it, it feels really natural for me – and that is the goal. Operators and syntax are important. They make a large difference to the feel of the language. So I decided to make it as obvious as possible, without sacrificing the internal consistency of the language. And ultimately the control is in the hands of the Ioke programmer.

So, to recap, any of the available operators can be defined in the regular way. Just define it like you would anything else. And you can assign macros or syntax to operators too. They are just handled as regular names.