The Crochet universe#
What are Crochet programs made out of? Well, many things, it turns out. First, Crochet programs are organised into packages. These packages will then dictate what other packages they need in order to work, as well as how much it trusts each of these packages. And packages will also contain modules—this is where we start getting deeper into “code”.
Modules are a collection of “code entities”. They may contain types, definitions, commands, tests, as well as several other less common things: relations, capabilities, actions, events, patches, and so on.
An application written in Crochet will usually be made out of several different packages—which in turn will have several modules, containing several code entities. But there isn’t really a technical distinction between “package” and “application” in Crochet. An application _is_ a package. The “root” package, to be more precise. It’s from there that Crochet decides what to load, and how much “power” each of the loaded packages should have.
If this sounds confusing, don’t worry. We’ll see this in more details, and with more examples.
Packages#
A package is the highest level of organisation in Crochet. A package can be your application, but more often than not, a package is a component—like a matching block—which you can combine with other packages to build bigger things.
They exist as a directory in your computer’s file system, and must
contain at least one file called crochet.json
. This file is what
tells Crochet what the package is all about. But a package will also
contain other files: modules, documentation, images, etc. The crochet.json
file aside, these can be organised freely inside of the directory—conventions
exist, but they’re just conventions.
crochet.json
#
So what’s this crochet.json
file anyway? It’s a JSON
file that describes what the package is, what it uses, and what it contains.
We call it, more colloquially, the Package Configuration.
The package configuration also tells Crochet where to find the things a package contains, as again Crochet doesn’t force items to be organised in any particular way.
This file might look like the following:
{
"name": "my-package",
"description": "A really cute package for Crochet",
"target": "browser",
"dependencies": [
"crochet.core"
],
"sources": [
"my-module.crochet"
]
}
So this tells us that we have a package called my-package
. It only works
in the web browser. It uses another package called crochet.core
(which
is distributed with Crochet). And it contains a single module, which can
be found in the file my-module.crochet
, in the same folder as the JSON
file.
We go into more details on what exactly all of these properties—like "name"
or "target"
—in the JSON file mean, and how one would go about reading
and writing these files in the Package Configuration section.
Directory structure convention#
Crochet isn’t picky at all about how you decide to structure your files—you will be describing that in your package configuration. However, there is a convention for organising them. By following the convention, you can make the life of people who’re using your package easier, if they want to learn from it or modify it.
So packages generally look like the following (directories are described with
a /
(forward slash) after their name):
o my-package/ (this is the package directory)
|
|---o crochet.json (the package configuration)
|---o source/ (modules will be placed here)
| |
| `---o my-module.crochet
|
|---o native/ (native modules will be placed here)
|---o test/ (additional tests will be placed here)
`---o assets/ (images, sounds, etc. will go here)
Modules#
So packages will contain modules. But what in the world are modules anyway? Well, they are files that tell Crochet what code makes up the package. Things are a bit trickier here than in other programming systems you may be familiar with because Crochet is a “language-driven” system. What this means is that modules may (and will) be written in different programming languages.
For example, our my-module.crochet
file from before could look like
the following:
% crochet
define greeting = "Hello";
command greet: Person = "[greeting], [Person]!";
This module provides two code entities: the definition greeting
, and
the command greet: Person
. Don’t worry about what these mean for now.
We’ll see all of this in more details later. The important thing to note
here is that modules are, essentially, a collection of these “code entities”.
So the code that a package contains will be all of these modules’ code entities
combined.
But modules are not aways these .crochet
files—because Crochet is
language-driven, modules can be written in many different languages, and not
just the Crochet language. For example, the following arithmetic.lingua
is also a module, ready to be included in a package and loaded as code:
% lingua
type Arithmetic =
| Addition(left: Arithmetic, right: Arithmetic)
| Subtraction(left: Arithmetic, right: Arithmetic)
| Number(value: Text)
grammar Arithmetic : Arithmetic {
Expression =
| left:Expression "+" right:Expression -> Arithmetic.Addition(left, right)
| left:Expression "-" right:Expression -> Arithmetic.Subtraction(left, right)
| value:number -> Arithmetic.Number(value)
token number = digit+
}
It looks nothing like our my-module.crochet
because it’s written in the
Lingua language, rather than the Crochet language. But the Crochet system
is able to load this code just as well as the Crochet one. The idea in Crochet
is that each module is written in the language that makes the most sense
for the task it’s solving—and the Crochet system will make sure they
can all be combined into a single package (and application), by automatically
translating between the new language (like Lingua) and the Crochet language.
Code entities#
So modules are a collection of these “code entities”. But what exactly are “code entities” anyway? They’re specific concepts that the Crochet knows how to interpret in order to make the computer behave in particular ways. They provide definitions and rules that direct computers, in a sense.
Collectively, these entities make up the “code” portion of a package, and they are further divided into many types of entities.
Types#
A type tells Crochet how to classify and structure some piece of information. They also play a key role in Crochet’s security and privacy guarantees. Here, a type is an unique, unforgeable name that has some structure associated with it.
For example:
type rectangle(width, height);
This type has the name rectangle
, and contains two pieces of information:
the width
of the rectangle, and the height
of the rectangle. These
pieces are also called Fields. We discuss types and their usage in more details
in the Data and Types chapter.
But how are names unique? And what exactly does it mean for names to be “unforgeable”? We discuss these in details, and what exactly these properties mean for Crochet’s security guarantees, in the Security chapter.
Commands#
A command tells Crochet how to compute—how to instruct the computer to do something. Not only that, but commands are the only way to instruct the computer to do something. Nothing happens in Crochet if not by the execution of a command—and this has some important security and usability implications that we’ll discuss in the Interactive Programming chapter.
Commands look like this:
command (X is rectangle) area = X.width * X.height;
This can be read as: “understand the area of a rectangle (we’ll call it X), as the width of X times the height of X.” It describes a rule to Crochet, and this rule tells Crochet how the area of a rectangle is computed.
The name of this command is not really area
though, but rather _ area
.
The underscore (_
) is used by convention to show where the arguments for
that command should go. In the body of this command, *
is just another
command! More specifically, the _ * _
command. Notice how X.width
and
X.height
both fill the underscore spaces—the way you define a command
is very similar to the way you use it.
Unlike types, multiple commands with the same name can exist in Crochet, as
long as they apply to different types—in the example above, the is rectangle
part is what tells us what types the command applies to. So we could have, in
the same module or elsewhere, something like:
command (X is circle) area = (X.radius ** 2) * pi;
Because Crochet knows what type is associated with each piece of information,
whenever we ask “hey, what’s the _ area
of this thing?”, Crochet will know
exactly which command needs to be executed, even though multiple rules for
_ area
exist.
We discuss commands and their usage in more details in the Commands chapter.
Traits#
If a type classifies one piece of information, then a trait identifies common aspects of many types. This commonality is, in Crochet, mostly captured by commands that can be performed on different types.
For example, rectangles and circles are different kinds of shapes.
But they both have a concept of an area
. This commonality can
be captured with a trait:
trait has-area with
command X area;
end
type rectangle(width, height);
type circle(radius);
implement has-area for rectangle;
command (X is rectangle) area = X.width * X.height;
implement has-area for circle;
command (X is circle) area = (X.radius ** 2) * pi;
Here we define a trait called has-area
, and the requirement for it is
that the type must understand a _ area
command. But just defining an
_ area
command isn’t sufficient—we also need to be explicit about
wanting the type to belong to this trait. That’s what we’re doing with
the implement has-area for ...
entities.
Why be explicit about it, though? Isn’t it obvious that rectangle and circle have an area? Well, it might seem so because we’re the ones defining it. But when we’re dealing with other packages—which might not even be aware of traits we’ve conjured!—, it’s easy to get into situations where types fulfill all of the requirements from a trait, but the commands don’t really behave as we expected.
If someone defines a type game-map
, and gives it an _ area
command
that provides the current area the player character is in, should game-map
really belong to our has-area
classification for geometric shapes? Quite
unlikely.
We discuss more about traits, including how and when to use them, in the Data and Types chapter.
Effects and Handlers#
Many programming systems try to make it easier to show things on the screen or interact with files. After all, what good is a program if you can’t see what it’s doing? Surely a program is only as good as the effects it has?
Well, Crochet takes a different approach. By default, no programs can show things on the screen, interact with files, or, really, do anything that someone would be able to observe. And there’s a very good reason for this: all of these things have a big impact on the security guarantees we can provide, and they can have very disastrous results: imagine writing a program to delete files and only realising you’d deleted the wrong one after it’s gone!
So, in Crochet, one describes what they expect the system to do—e.g.: showing things on screen, or deleting files. But how these things are carried out is decided later, and can very well be replaced. For example:
effect display with
show(text);
end
Here we tell Crochet that we have some intention of showing text on the screen,
and later we can ask Crochet to do so by performing this: perform display.show("Hello")
.
But how Crochet actually goes about performing it is defined elsewhere,
through handlers:
- handler show-on-transcript with
- on display.show(Text) do
transcript show: Text; continue with nothing;
end
end
Effects are certainly one of the more difficult parts of Crochet. And so they get their own chapter, which explains, in depth, why they’re important, how they’re used, and why they are the way they are.
Capability groups#
Capabilities are how Crochet is able to guarantee that programs can be safe even if you add code from the internet to it. They also allow one to reason about a program—and then decide whether you trust it or not.
A capability group is an entity that describes some kind of power—or capability. For example, we might think that deleting files is a dangerous thing. What if someone sends you a Crochet package, telling you they’d love if you could help beta-testing their new game, and then you run it, and it deletes all of your personal photos. That wouldn’t be cool.
If, instead, we have a capability for deleting files, then Crochet would tell you beforehand: “Hey, this game you’re running can actually delete your files. Do you want to run it anyway?”.
For example:
capability delete-files;
type file-system;
protect file-system with delete-files;
Here we do three things: tell Crochet that “deleting files” is a thing, and
is a very dangerous thing. We then introduce a file-system
type, and tell
Crochet that by using file-system
one would be able to delete files. This
way Crochet can accurately track these dangers and only show you relevant
ones.
Of course, it’s not just because something can do dangerous things that it is inherently evil—but we discuss what exactly it means, and what goes into deciding whether to trust some application or not in the Security chapter.
Tests#
Tests are an important part of developing any software. Crochet supports tests as just any other code entity, and provides tools for running them.
They look like this:
test "Addition" do
assert (0 + 1) === 1;
assert (1 + 0) === 1;
assert (2 + 3) === 5;
end
We discuss testing and Crochet’s support for tests in the Testing chapter.
Native modules#
So, the Crochet system only speaks the Crochet language natively. Sadly, the Crochet language is very limited. For example, it doesn’t even have any concept of arithmetic addition! Let alone a concept of drawing things on a screen.
How exactly do Crochet applications get to do anything? That is, if
I can write 2 + 3
in Crochet and get 5
as a response, how exactly
does Crochet know what to do there, if it doesn’t know what an arithmetic
addition is?
Well, someone needs to teach Crochet what to do in these cases. These missing concepts are often added to Crochet by using a different language—a language that the computer speaks natively. That’s the role of a Native Module. Instead of being restricted by what Crochet can do, they are restricted by what the native language can do. Most native modules in Crochet are written in JavaScript.
Because native modules aren’t restricted by Crochet’s rules and limitations, they are very powerful. And all of this power is dangerous. In order to make Crochet safe for everyone, the use of native modules is carefully controlled through Capabilities.