Why MiniScript?

As more and more people discover MiniScript, the question is occasionally asked: why? Why was MiniScript created, and why should someone use it rather than some other language?

It's a perfectly valid question, so in this post I will try to clarify. My purpose is not to convince anyone of anything, but to explain why, after years of using other scripting languages, I felt compelled to create something new. I'll do this without attacking other languages, since every language has its fans and I don't want to kick anybody's dog; instead I'll focus on what I think is really cool about MiniScript, and how it offers a collection of advantages that, taken together, can't be found anywhere else.

Background & Influences

I come from a programming history that includes significant time coding in assembly language, BASIC, COBOL, Pascal, C/C++, LISP, C#, Perl, Python, Java, JavaScript, Lua, and a few more obscure ones like MOO and FORTH. I spent ten years as the chief engineer at Real Software (now called Xojo), and led the development of the second-generation compiler for REALbasic, a commercial high-level, strongly-typed OOP language. I was quite active in the Python community for many years, and got about halfway through writing a book about Lua before I just couldn't take it anymore.

All these languages have something to offer. Here are some of the features I especially liked, and which influenced the design of MiniScript:

  • Python: elegant handling of strings, lists, and maps; a nice REPL
  • Lua: small, easy to embed in other software
  • REALbasic: simple, punctuation-light syntax
  • C#: seamless Unicode support; very consistent and "guessable" APIs

There are of course many purely aesthetic choices that must be made in any language too, but those don't matter as much, except where they cause schisms in the community (like curly-brace placement in C-derived languages, or tabs vs. spaces in Python). What matters most is that a language be (1) easy to access (i.e. easy to embed, if that's your need, or easy to download or otherwise access as a user), (2) easy to learn and use, and (3) powerful enough to enable serious use. There are many design choices that are not strictly aesthetic, and have real consequences for these three main points.

Easy to Embed

MiniScript is a small open-source library written in both C# and C++ (and there's even a recent third-party port to Kotlin!). My original need was in Unity, so I wrote it first as a Unity plug-in. Because the MiniScript compiler/interpreter itself is written in C#, it integrates seamlessly with other C# code, allowing you to do things like easily wrap Unity types in MiniScript values, and vice versa. It's thread-safe and can also be time-sliced within the main thread. User (MiniScript) code is completely sandboxed and safe, but it's trivial to add new intrinsic functions that call into your own C# code exactly as needed. MiniScript strings are C# strings, so there is no needless conversion. It all just works, exactly as you would want it to.

The C++ port is newer, and hasn't had as much real-world use yet, but it will allow the same sort of integration with Unreal Engine, or any other environment based on C/C++. The C++ version is also the basis of command-line MiniScript, and underlies the web-based "Try-It!" interface. The C++ code is lightweight and does not rely on the STL; it will work even on smaller systems like Raspberry Pi, or even microcontrollers. Extensive unit and integration tests ensure that the C# and C++ versions behave the same way, so users never have to worry about what they're using.

Both versions are open-source on GitHub.

Clean, Minimal Syntax

There are languages where, to the untrained eye, it appears someone has sneezed a mouthful of punctuation all over the screen. This is intimidating to new users, and needless opportunity for mistakes for more experienced users. At the same time, we don't want to pointlessly spell things out; > is a perfectly fine way to represent "is greater than," and is actually clearer and faster to read than the English words.

So in MiniScript, you'll find only a few bits of special punctuation: square brackets for indexing, curly braces to define a map, and parentheses only where they're really needed. Mathematical operators are all what we learned in school: +, -, *, /, plus the standard comparison operators >, >=, etc. For equality testing we follow the standard from C-derived languages: == and !=.

And at every opportunity, we try to reduce the punctuation needed; you'll find no semicolons at the end of lines, no curly braces of ambiguous placement, no parentheses around the argument of a print (or any other) statement.

Here's an example of MiniScript code:

for i in range(10, 1)
   print i + "..."
   wait
end for
print "Liftoff!"

This clean, punctuation-light syntax lets users focus on what their code is actually doing, without mystifying things with a lot of extra stuff.

Four Central Data Types

One thing that became very clear, over years of using various languages, is that four core data types are absolutely essential:

  1. Numbers. (Obviously.)
  2. Strings.
  3. Ordered, mutable lists.
  4. Maps (aka dictionaries; key-value pairs).

These represent fundamental needs that come up again and again in all kinds of programming tasks. When you don't have them, you end up writing awkward work-arounds to make up for it. Conversely, when you have these, there is very little else that's really needed; lists and maps in particular are powerful enough to substitute naturally for most other kinds of containers (stack, queue, set, etc.).

So, in MiniScript these are the four central data types. There are couple others (like functions, which are first-class objects), but these are the ones the language is designed around. As in most modern languages, MiniScript strings are immutable (which avoids some very tricky bugs) and fully Unicode-based (so they work worldwide).

(Python is a language that handles these data types very well; you'll notice a lot of the syntax and functionality for working with them in MiniScript is similar to that of Python.)

Consistency

A good programming environment is guessable: having learned things A, B, and C, you can guess how to do thing D and have a good chance of getting it right. This comes from being consistent wherever possible in syntax and naming conventions. So in MiniScript, once you've learned that + is used to add numbers and join strings, it's easy to guess that it's also used to join lists or combine maps.

MiniScript applies this principle all over the place; whatever might make sense to do, MiniScript probably does. For example, the * operator used for multiplying numbers also works for strings and lists (just like applying + multiple times).

The same principle is applied throughout MiniScript's design. .len gets you the length of a string, list, or map. .indexOf gets you the index of any element in a string, list, or map. .shuffle randomizes a list in place; for a map, it shuffles the values associated with keys. You end an if block with end if, and a while loop with end while, so I bet you can guess what ends a function or a for loop.

Modern Language Features

While we want a language that is tiny and easy to learn, we don't want to leave out important modern features that make coding easier. Here are some in MiniScript:

First-class functions

Functions in MiniScript are a fifth data type (after numbers, strings, lists, and maps). That means you can assign them to variables, pass them as arguments, use them as keys or values in a map or elements in a list, and generally use them like any other value. People used to other languages often remark on the seemingly unusual syntax for declaring a function in MiniScript:

triple = function(n=1)
   return n*3
end function

This is actually just an ordinary assignment statement, assigning a value to a variable called triple... it just so happens that the value being assigned, in this case, is a function literal. (This is yet another example of consistency: if you already know how to assign a number or a string to a varible, then assigning a function is no different.)

It goes without saying that MiniScript functions support recursion (though with MiniScript's strong list support, many problems commonly taught in CS classes with recursion can be solved more flexibly with iteration).

Unicode support

I mentioned this already, but it's worth mentioning again. Text in today's world should be Unicode, period. With Unicode strings (even in the C++ version!), MiniScript is ready to handle any language in the world, not just English.

Object-oriented programming

Classes in MiniScript are a simple extension of maps. This supports single inheritance, avoiding the thorny issues multiple inheritance can cause. But like Python, MiniScript uses duck typing to allow a class to adhere to any number of "interfaces." A couple of additional keywords (new, self, and super) are all that's needed to provide simple but powerful OOP support.

The Mini Micro API (which is not part of MiniScript itself, but rather a sophisticated example of an embedded MiniScript environment) uses OOP extensively. So to create a sprite, for example, you simple write s = new Sprite, and then assign to properties like s.image, s.rotation, and so on.

Variables local by default

This is a big one. In a language where variables are global by default (like old-school BASIC), it is very easy to make code that works when you write it, but later causes things to break in mysterious ways, because it has inadvertently clobbered a variable used by some other code. For example:

vowelsInWord = function(word)
    count = 0
    for item in word
        if "aeiou".indexOf(item) != null then count = count+1
    end for
    return count
end function

totalVowels = function(lst)
    count = 0
    for item in lst
        count = count + vowelsInWord(item)
    end for
    return count
end function

print totalVowels( ["foo", "bar"] )

This seems like reasonable code, right? But in a language where variables are global by default, the vowelsInWord function would work fine on its own, and the totalVowels function would work with a slighly different version of vowelsInWord, but as written, they don't work together. The internal computations of one can clobber the other. And the only way you can know this is to look at them both at once. Global variables make programming hard because you have to keep far more of the program in your head at once, and they make this sort of subtle error far too easy to commit.

But in MiniScript, variables are always local to the function you're in, unless you explicitly reference globals.varname (that consistency again — globals is just a map, and you use it like any other map). So the above example works fine (try it!).

Read-Eval-Print Loop

Last year I was chatting with a computer science professor about teaching kids to program, and he opined that it's barbaric to ask anybody to learn to code without an interactive Read-Eval-Print loop (or REPL). I couldn't agree more.

For those not familiar, a REPL is a prompt where you can type a bit of code and press Return, and it is immediately executed. (Or if you've started something like a loop, it will wait for you to finish the loop and then it is executed.) If what you tried doesn't work, you arrow-up and try again. In any case you get a result that does something, or teaches you something, and based on this new insight you type something else. It's immediate, interactive, and an incredibly powerful way to learn.

MiniScript fully supports a REPL. Whether you actually get one depends on where it is embedded; the Try-It! page doesn't have one, but Mini Micro sure does, as does the command-line version. This is a must-have feature for any language you want people to play with and enjoy learning.

Right-Sized

Finally, MiniScript is carefully and thoughtfully constrained. The core can be described in a one-page reference. That's not just a gimmick; it's key to making the language (and its core API) approachable. New users rightfully worry about all the stuff they don't know they don't know. When you're dealing with something huge like C#, JavaScript, or Python, that's a very legitimate concern — the amount of material to learn seems bottomless, and who's to say there isn't something important and useful in all that stuff you haven't learned yet?

So MiniScript works hard to stay mini. Every feature, every function was carefully weighed to see whether it really deserves to be there. Anything that is commonly needed and hard to write (or hard to do efficiently) yourself is there; there are functions to split a string into a list, join a list back into a string, do all the standard math functions, etc. But things that are more esoteric, or easily defined in terms of other functionality, was left out. The only loops are for and while; the only branching is if/else, break, and continue. They're all you need.

Of course when embedded in any particular context, the host app can easily add more functionality. Command-line MiniScript has APIs for accessing files; Mini Micro has APIs for graphics and sound, so you can make games like this one. Other games and applications add APIs particular to their own context. But the core remains simple and easy to grasp.

OK, but why a chinchilla?

MiniScript logo

That cute character to the left is Minnie, the MiniScript chinchilla. The chinchilla was selected for the mascot because they have a lot in common with MiniScript:

  • they're small and adorable;
  • they're well-behaved;
  • they're not yet widely known, but
  • those who know them, love them.

Conclusion

That's the story. Each of the features above can be found in some other languages, but none of them could be found all together. When I wanted to embed a scripting language into my Unity game, or select a friendly, interactive learning environment to teach my kids, I couldn't find anything that had all the features I wanted. So MiniScript was born.

Thanks for sticking with me this long; I know this was a long post. And I hope if you ever find yourself looking for a language to embed, or a fun, easy language to learn or teach, you'll give MiniScript a chance!