Minima is a programming language. There are two things that it’s important to know about Minima.

  • It’s a very small language
  • It’s not a very good language

You can find a Java implementation of it here.

It’s called Minima because it’s small. It’s not as small as it could be - it’s not technically minimal. But it’s pretty small.

Minima is a language that should only be used in one circumstance: as a tool to better understand programming languages. Its raison d’etre is to be used as a base on to which language features are added, with as little complication as possible. Then, once a given feature has been added, it is then removed and for the next feature, we start with a blank slate again.

Let’s start with a quick tour of Minima.

print["Hello, World!"]

It’s traditional to start any introduction to a language with the archetypal “Hello, World!” program. This is Minima’s. It’s pretty minimal. Let’s break it down.

print is a variable, bound to a function defined in the Prelude - a collection of pre-defined utility functions and objects, available in all programs. In fact, print is the only function in the Prelude.

“Hello, World!” is a string literal. Anything enclosed in double-quotes is converted by the runtime into a string value.

Square brackets are used for function invocation. I acknowledge square brackets are an unconventional choice here, but there are good reasons for that. I’ll get into that in the next post.

Anything within the brackets - be it one argument like “Hello, World!” here, multiple comma-separated arguments, or even no arguments at all - is passed to the function, which then uses those to do whatever that function does. In the case of print, it displays the text in the console and returns SUCCESS.

SUCCESS is an object defined in the Prelude. In fact, it’s the only object in the Prelude. We’ll get to objects in a bit.

We can also define our own functions:

[first, second] => first

This is a function which takes two arguments, and returns the first of them. Why would you ever bother defining a function like that? There are reasons, which I’ll get to in a moment.

Everything in square brackets is the parameter list, then you have the fat-arrow symbol, and then you have another expression. When the function is called, the arguments are bound to the parameters, which are then available as variables in the body of the function. The expression is evaluated, then returned to the caller.

Every function returns a value - there’s no concept of a void function. When we don’t have any information to return - like in the print function - we return SUCCESS. This is akin to returning () in many functional languages - it’s a type which conveys no information, which has only one possible value.

We’ve seen variables, both from the Prelude and inside function bodies. We can also define our own variables:

message is "Hello, World!"
print[message]

We can define a variable by introducing a name - message in this case - and then binding an expression to it with the is keyword. From here on, any use of message will be resolved to “Hello, World!”.

We also see we can have multiple expressions in a program. This also allows us to do things like this:

print["Hello, World!"]
print["Oh, just one more thing..."]

What if we wanted to do that in a function? A function just takes a single expression as its body. For that, we use the group.

printBoth is [first, second] => (print[first], print[second])

Here we’re defining a variable and assigning a function to it - so far so good, we know all about that. The body does something new though: it groups two expressions together, comma separated within parentheses.

A group is an expression composed of a sequence of expressions. When evaluated, each expression in turn is evaluated, and the value of the group is the value of the last expression in the group.

The evaluated values of the expressions prior to the last expression are discarded, so it only makes sense to group expressions which have side effects - such as introducing new variables or printing to the screen.

We can also do this:

point is { x : 2, y : 3}

This creates a new object: a set of key-value pairs, using : to separate the key from the value and , to separate the pairs, enclosed by curly braces ({}). We also see that we can put numeric literals in our programs - we’ll talk more about those in a moment.

We can then access the fields, also using the : operator:

printX is [point] => print[point:x]

We can use objects to build larger data structures from smaller ones, but we can also use them to bundle behaviours. For example:

greet is [name] => print["Hello, ":concat[name]]

Here, concat is a field on the string “Hello, “, and it’s a function which takes another string and concatenates it on the end of “Hello, “. We can use this philosophy to build bundles, not of state, but of behaviour.

greeter is [intro] => {
  greet : [name] => print[intro:concat[name]]
}
french is greeter["Bonjour, "]
french:greet["Jacques"]

You may wish to call fields that are functions “methods”. They’re not exactly the same, but then two given features with the same name across two different languages aren’t often the same thing anyway.

That’s the entirety of Minima. Summing up, we have:

  • Declaration and evaluation of variables
  • Definition of and invocation of functions
  • Definition of and accessing of objects
  • Grouping many expressions into a single compound expression
  • String and number literals
  • print and SUCCESS in the Prelude

And that’s pretty much the entirety of Minima.