For last few weeks I have been exploring a relatively new language (about 2 years old) Crystal (hasn’t hit 1.0 as of today). Crystal is inspired by Ruby and it has Ruby like syntax that we all love. It is a statically typed language, so you won’t have runtime bugs since the compiler will catch type related bugs during compilation.
Crystal is very efficient in compiling code to native code which can be distributed as binary.
Crystal has a modern standard library which has implementations of markdown and websockets baked into it.
And while we are in introduction territory, it’s important to know that Crystal is self-hosted. That means
The Crystal is 100% written in Crystal.
- Everything is a object just like Ruby.
- There are two types of objects, Values and References.
- Value objects includes Number, Bool, Nil , Char, Struct, Tuple and Pointer.
- Rest all objects are References including String, Array and Hash.
- Value objects are passed by value and Reference objects are passed by reference.
Now that we’re over with the introductions, let’s dig a little deep into language core constructs and concepts.
Global Type Inference
Crystal determines the type of objects and return types of functions using mechanism called global type inference. The process includes traversing the whole AST and assigning type to each node based on type expression it represents.
Let’s catch up on curious case of Union Types.
In above code snippet the type of variable name is determined by type inference. The AST node representing first conditional expression will have type of String associated with it while the AST node representing second expression will have Boolean type associated with it. The variable name hence will have a union type String | Boolean associated with it.
Let’s take another example of Union Type.
foo = ["foo", false, 1, "bar"]
typeof(foo) #=> Array(String | Int32 | Bool)
In above code variable foo will have type Array(String | Int32 | Bool).
Method overloading has three different criteria.
- Number of arguments.
- Types of arguments.
- If methods accepts a block or not.
Let’s take an example of addition.
The last line in above code snippet will throw below error.
no overload matches ‘String#+’ with types Int32 Overloads are: — String#+(other : self) — String#+(char : Char)
During the compile time Crystal determines all the possible overloads and throws errors if type of arguments supplied doesn’t match the possible argument types available in overloads.
You can also pass type of arguments in methods like this
Let’s try to fix the String and Number overloads situation.
This works perfectly fine since now we have a overload that can handle argument of type String and Number.
Moving right along!
- stack allocated
- passed by value since Struct inherits from Value.
while class is heap allocated and passed by reference since it inherits from Reference. These features makes struct more suitable for defining a type in. For example if you want to build a vehicle, the implementation would look something like this.