Share your repls and programming experiences

← Back to all posts
Curta - Let's make hard things easy

Note: There is a [fairly important] jam-related note at the bottom of this post.

Programming on Arduino has always been with C++. And that makes sense — A tiny microcontroller with highly limited memory has to be written in a low-level language, right?

Well, no. Enter Curta.

So, what's Curta?

Curta is a statically typed (but often dynamically inferred) event-driven embedded systems language designed to be intuitive and easy. But don't think "easy" means "simpler" — Curta doesn't remove needed features or try to oversimplify things. Instead, it gives you the power of Arduino's C++ in an easier-to-use package.

And, Curta does this while keeping memory usage relatively low. In an example program (in fact, the first example below), Curta used only 5% more sketch and global variable memory. Not bad for a type-inferred, high-level, event-driven language!

So what's all this about event-driven?

Curta is an event-driven language — i.e, when events in the outside world happen, you describe how Curta should react. For example, say you wanted to wait for a pin to go low and then do something. In Arduino C++, you'd do this:

But this is bad, because it blocks everything else in the program! In Curta, you react to the PinLow event:

That might look a little confusing, since you probably don't know what the Curta syntax is, but you probably get the idea.

Ok cool — so what else is special about Curta?

Glad you asked!
Curta has a variety of benefits:


Curta compiles to C++, so you don't need any special toolkits. This also makes it portable: if the system can run C++, it can run Curta.

Simple, consistent syntax

From the above, you might think that Curta's syntax is confusing. But really, there are only three simple constructs that the entire syntax uses.

Easily extendable

By no means am I going to claim that Curta is done. But what I will claim is that Curta can be easily extended — that is, external environment code can be written and Curta can interface with that code.

I could keep listing things, but don't worry I'll spare you :D

Phew, thanks! So... can I see some example code?

Sure! No language description is complete without some examples.

Suppose you want to echo pin data from one Arduino to another by using Serial. (Basically, one Arduino monitors pins as inputs. If any of the pins go high, it tells the other Arduino to set its corresponding pin high. It does the same for pins going low.) Here's how you'd do it in Curta:


And on the receiver:

You probably have a feel for how Curta works now, but I'll explain this for those interested:

Something of the form an_expression :some_name {optional args} {maybe more args}... is called a query. Queries allow us to manipulate objects or get data from them. For example, if I wanted to get the object for Pin #13, I would do:

What's the question mark for?

All arguments in Curta are named arguments by default. To suppress this, you can use a ? in place of the argument name. So, the above is equivalent to:

because the name of the argument to get on object Pins is "pin".

I see some big queries, what's the deal with those?

There are two types of queries:

  • Query expressions. These are like the example above, and they return a value.
  • Query statements. These do not return a value, but instead provide a shorthand syntax to perform many queries on an object.

The "big queries" are query statements. Here's an example of one of those:

Query statements utilize whitespace (audience gasps) to determine their structure. The above query becomes a tree:

So the [init INPUT] query is performed on the return value of the [get 13] query being performed on [ Pins ]. Seems complicated at first, but it's actually super powerful and useful.

Why do some queries not have arguments?

Because some queries don't need them! For instance, size on a List:

This performs the size query on myList. (This probably explains the name "Query" more.)

So... what's the deal with the if-statement?

An if-statement (and the loop-statement) both can be queried! In fact, that's the only way you can do anything useful with them. For instance, suppose I wanted to translate the following C++ code to Curta:

In Curta, that becomes:

...which gives a sense of ownership to the "if".

But... why the random curly-bracket?

Ah yes. Well, because there's no easy way to tell the difference between


without it.

I have more questions!

Great! Feel free to comment on this post, I'll answer them as promptly as I can. There's also a lot we haven't covered here (like the templating, delays, forcing events, the loop statement, loop jumps, exit...), so if you're interested I can explain that.
EDIT: I've updated the post to include some more about the language.

I want to get coding!

Great! Head on over to, make a fork, and read to get started. You do need an Arduino or other Arduino-compatible board. You'll also need to install the LinkedList Arduino package to run Curta.

I want to learn more!

Awesome! Let's take a look at some of Curta's other features. Note: this is getting into the more complicated parts of Curta, if I've explained it poorly please comment and I can clarify.

The object system

In the above examples, we've already been using Curta's object system. However, we haven't used any custom objects, which is what this will cover.

Let's make an object which holds an int and a string. You should be allowed set the int and the string or get the string (but not the int). Why are we doing this? Well, it provides a nice example :D

To accomplish this random task, we do two things:

First, we declare the structure of our new type. We see it has two properties: @inner_theint and @inner_thestring, prefixed with "inner_" so that we can make a query called "thestring" and not have overlap. (note that property declarations MUST come before query declarations). We now see that properties are preceded by @; contrast this to arguments, which are always preceded by $. All object properties are always private. This is not a limitation, it is a feature — more on that in the next section. Next, we define the possible queries on this type: it can be setted, or we can get thestring.

Now we must declare how these queries work:

Here, we first declare that we are implementing the set query. Recall that above, we defined the arguments to :set as "newint" and "newstr". Here we reference these with the $ sigil — recall that arguments are referenced with $ while properties are referenced with @. We also see a new keyword — new (haha pun). new introduces mutability into Curta, by allowing re-assignment of properties. Here we assign $newint to@inner_theint and $newstr to@inner_thestring, respectively.

The second query, :thestring, is fairly straightforward.

So how can we use this? Well, we create an instance of MyWeirdContainer:

We can make as many instances as we want (this is the same thing as our first example, where we made instances of the List type, except now it's our custom type. In fact, List is technically a custom type, but defined in std.rta as an external environment type.)

Something easier — variables

Now that we've discussed perhaps the most difficult part of Curta, let's talk about something easier: variables. Variables can be introduced with the let statement:

They work mostly the way you expect. But if you try something like this, you'll get a LexingError:

It should throw an error on the =. Why? Because Curta has no re-assignment operator! Once you have declared a variable with let, you cannot mutate it. This brings up two important points:

  • Curta has no mutable variables.
  • All mutability (and in some respects, functional impurity) is contained inside object properties.

So what does this mean practically? Well, it means you can just use one global object to store all your variables in. However, you also can write every Python program in one line of code and name all your variables gibberish, but that's not good practice. Instead, Curta encourages an "encapsulated" style of programming — you put everything related to one aspect of your program (say, a temperature meter) in an object, keeping the mutable variables out of the global namespace.


Curta has built-in extensibility features. You can write C++ code in the outside environment and Curta can interact with that code. There are two ways for Curta to interact without outside code:


These are things like Arduino's INPUT and LOW. They can be imported by simply doing the following:

(Note: std.rta already defines these for you, this is just an example)


This is how you can import more complicated things (such as methods) into Curta. Suppose you have the following C++ code:

We can import this into Curta with the following:

Note that we don't need to declare the property a, since all properties are private!

We also must physically import that C++ code, so you would paste it at the top of Curta's generated C++. (If not, Arduino's C++ would throw a compiler error SomeStruct does not name a type when you try to make a new instance of SomeStruct).

Wrap up

That's all I'll cover here. Like I said above, if you have more questions feel free to comment and I can explain more parts of the language. While the learning curve may seem steep, it actually becomes intuitive once you use it enough.

Something weird happened, what do I do?

Just comment on here with the bug, I'll fix it as promptly as possible! Make sure to always use, it will contain the most up-to-date version.

You said something about a Jam note...

Oh right, almost forgot! Two things:

The team is @curtadev, repl is However, all new curta developments will happen on
Thanks to @hydrobolic for lots of feedback and suggestions.

Judges note: I'm just entering this for fun mainly. Why? Because I don't think Curta lends itself well to The goal of is to be able to get coding quickly, all online. But Curta requires a physical piece of hardware to run on... so you can't fulfill the "get coding quickly" or the "all online" part. However, I wouldn't be opposed to winning one of the "individual prizes and categories" :D


why in the world did this only get 4 upvotes...


@LeoSekour To be honest, I have no idea. I spent a lot of time on this.


@fuzzyastrocat yeah this is an amazing project its sad that barely got any attention


It seems like it would be relatively easy to use c++20 modules to automate the insertion of C++ extensions...?


@Highwayman Yes, that is possible. However, for simplicity of setup in the Arduino IDE I said to copy-and-paste here, but for more experienced users yes C++ modules can organize things better.


Really cool language man! Great way to add events to Arduino, you should post it to Reddit! I don't have an Arduino, but for people that do it I'm sure it'll be useful!


@yekyam Heh, I don't have a reddit account :P
But glad you enjoyed it! I really think event-driven programming is the best thing for embedded systems... I mean, nothing in the real world happens linearly, it's all event->reaction.


@fuzzyastrocat So with Curta, can two events happen at the same time?


@yekyam Unfortunately no. Nothing ever can without multicore computing — and of course, the Arduino has no such capability.

This is why much of the mutability is encapsulated — it reduces the chance that two events executing sequentially will have an unwanted outcome.


@fuzzyastrocat Makes sense, just wanted to see if you had hacked together some thread-like way of executing. Still though, great work!


@yekyam Yes, a more thread-based system was intended (but with the time constraints of the Jam I couldn't fit it in). If implemented it will also allow for events to await other events.


@fuzzyastrocat Still though, great work, and I hope to see more progress on it soon!


As said in the above post, if you have any questions ask here!


@OrangeJooce123 This is an odd place to put that, but ok?


@fuzzyastrocat the jam required you to enter as a team of at least 2 members, could you please edit your post and mention who your team member is?



Thanks to @hydrobolic for lots of feedback and suggestions.


@fuzzyastrocat thank you.


@TheDrone7 No problem!