Gradual representations#

Expressions in Crochet deal with values, but every value in Crochet has an associated type. These types are organised in a hierarchy, which allows us to talk about more general characteristics of values by simply moving up in this hierarchy—just like we can talk about flowers in general and have that still include specific flowers, like roses or lilies.

The any type#

At the root of this hierarchy is the type any. Thus, every value in Crochet also belongs to the any type. Anything that we talk about when we talk about any must—and will—apply to every value in Crochet.

any itself is an abstract type, so it can’t be constructed. The value that represents the type any can still be accessed through #any, but there’s rarely any use for this.

The unknown type#

The unknown type is a container for all types of values which has two primary functions:

  • To be able to represent arbitrary values coming from the outside world (e.g.: through the foreign interface) in the Crochet space, but without really modelling that value in Crochet; and

  • To be able to hide the concrete type of a value, so it can be passed around between two places in the code without any of the intermediary places having any knowledge of what this value is, or how to manipulate it—this is a very important feature for type safety and security.

The first use case is covered in details in the Foreign Interfaces chapter. But what these two use cases mean is that unknown is pretty much a kind of black box—you can pass the box around, but you cannot know what is inside of it.

From the Crochet side, values can be turned into this black box with the _ as unknown command. So 1 as unknown creates a black box containing the number 1—a fact that is known internally in the Crochet VM, but not observable by the Crochet analysis tools, or really anyone else.

In order to recover the value from the black box in the Crochet side, we need to know what lays inside of it. We once again use the _ as _ command for this: unknown as Type will allow us to recover the value from the black box, as long as we know what Type is. And then we will be able to manipulate the value only with the the powers that Type grants us.

For example, imagine we first box the integer 1. Then we unbox it as the type any—this will not give us back a value of type integer, but a value of type any! This means that we’ll still be unable to use arithmetic operators with the unboxed value, as any doesn’t really know what _ + _ means.

Internally, whenever there’s a mismatch between the type we request and the type of the value in the box—but the type we request is still acceptable—, Crochet will use a concept called Sealed Types to represent the value with the exact type we requested, hence preventing anyone who does not know the specific type to gain access to its powers.

A note on overwrapping#

What happens if you try to run (1 as unknown) as unknown? That is, to take something that is already a black box, and then try to turn it into another black box.

In Crochet, this operation will give you the same black box. That is, you cannot put a black box inside of a black box. You can’t have an unknown value that contains another unknown value.

The sealed type#

Warning

Sealed types are not implemented in the Crochet VM yet.

A sealed type is a box for values that allows Crochet to see them as a different type from their specific one. That is, if we have 1, we could seal this value with the any type, and we would get an integer value 1 which, as far as any Crochet code can observe, acts exactly like the any type, and nothing more than that.

The nothing type#

The nothing value is a singleton that is used whenever there is no other more interesting value to be used.

For example, when we define a command like the following:

command i-dont-do-anything: _ do
  // No, really, I don't do anything
end

Whenever we use this command (e.g.: i-dont-do-anything: 1) we’ll get a nothing value back. That’s because commands always need to provide some kind of result, but because we didn’t really add any result to this command definition, Crochet provided something for us.

nothing is also commonly used when we may or may not have a value. For example, it’s common to create hierarchies as follows:

type branch(more-general, name);

And we could then define a hierarchy like the following:

let Family = new branch(nothing, "Felidae");
let Subfamily = new branch(Family, "Felinae");
let Genus = new branch(Subfamily, "Felis");
let Species = new branch(Genus, "Felis Catus");

In this snippet of the scientific taxonomy of domestic cats, we’ve chosen to stop the hierarchy at the Family level. That means that, if we start from the domestic cat, the more general we can get is the family Felidae. And we indicate such by saying that there’s nothing above it.