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.
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
any itself is an abstract type, so it can’t be constructed. The value
that represents the type
any can still be accessed through
but there’s rarely any use for this.
unknown type is a container for all types of values which has two
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
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
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
any—this will not give us back a value of type
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
_ + _ 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.
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
could seal this value with the
any type, and we would get an integer
1 which, as far as any Crochet code can observe, acts exactly
any type, and nothing more than that.
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
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.