Thanks for the advice, Crystal compiler!
Tags: crystal, tutorial, nil, oop
Published: 2020-01-07 19:03:12

In this post, the compiler is presented as a tool to help the programmer to design a good model. Although the example is very simple and its domain limited, it is easy to extrapolate the solution to equivalent situations in more complex problems.

So, let's start presenting the problem.

# The problem 

_Our objective is to model a parking lot where vehicles can park. Each vehicle will have an unique license plate (for example ABC123). The license plate will be the way to identify each one of them. It is important to note that we do not care about the type of vehicle (it can be a car 🚗, motorcycle 🛵, truck 🚚, etc. 🚀). Finally, at any time we can ask the parking lot to list the license plates of the parked vehicles, and also we could asked if it's empty or not._

For example, we will want to write something like this:

```crystal
parking_lot = ParkingLot.new
parking_lot.empty? #=> true
parking_lot.licenses_plates #=> []

parking_lot.receive(Vehicle.new "ABC123")
parking_lot.empty? #=> false
parking_lot.licenses_plates #=> ["ABC123"]
```

For this post **we are only going to implement the model of the vehicle**, i.e we are only going to focus on this part:

```crystal
Vehicle.new "ABC123"
```

We are leaving the parking lot implementation for a next post ... maybe?

Also, we are going to use the [Crystal](https://crystal-lang.org/) language (luckily or the title would not make any sense 🤔) with all the advantages that Crystal gives us for being a statically typed language, and also with some advantages of its own: [its type inference system](https://crystal-lang.org/reference/syntax_and_semantics/type_inference.html) at compiling time. 

There are some techniques that we could use to start implementing the model (for example TDD) but in this case let's put all our attention to what the compiler is going to tell us at each step.

# First ~~solution~~ attempt

We may start with something like this:

```crystal
# vehicle.cr
class Vehicle
  property license_plate
end
```

**Note:** we use the macro [property](https://crystal-lang.org/api/latest/Object.html#property%28%2Anames%2C%26block%29-macro) to define the vehicle's license plate.

Given the above model, this is how we could use it:

```crystal
# app.cr
require "vehicle.cr"

vehicle = Vehicle.new
vehicle.license_plate = "ABC123"
```

Let's compile it with:

```shell-session
$ crystal app.cr
```

aaaand 🥁

⚠️ Oops! the compiler (twist &) shouts ...

```shell-session
Error: can't infer the type of instance variable '@license_plate' of Vehicle

The type of a instance variable, if not declared explicitly with
`@license_plate : Type`, is inferred from assignments to it across
the whole program.
```

Ok, so **the compiler could not infer the type of `license_plate`** by the use of it in the code ... 🤔 Maybe, we could be more explicit declaring `license_plate`?

# Second attempt: explicitly declaring the type

As the compiler could not infer the type of `license_plate`, we are going to explicitly declare the variable with its type:

```crystal
# vehicle.cr
class Vehicle
  property license_plate : String
end
```

Let's compile it again ...

```shell-session
Error: instance variable '@license_plate' of Vehicle was not 
initialized directly in all of the 'initialize' methods, 
rendering it nilable. 
Indirect initialization is not supported.
```

Oh no! 😱

The compiler is telling us that we had not initialized `license_plate` in any `initialize` methods so there is a moment when `license_plate` has the value `nil`. But when declaring the variable, we have indicated that **is** a `String`. And this is a great feature of Crystal: [nil](https://crystal-lang.org/reference/syntax_and_semantics/literals/nil.html) not only has its purpose: is used to represent the absence of a value, but also has its own type: `Nil`. And **`Nil` is not equal to `String`**. 

This is very easy to test, let's ask the compiler itself!

_Please Crystal, tell us what's the type of the value `nil`?_

```shell-session
$ crystal eval "puts typeof(nil)"
Nil
```

_Please Crystal, tell us if `nil` is a `String`?_

```shell-session
$ crystal eval "puts nil.is_a?(String)"
false
```

Perfect! The value `nil` has type `Nil` and is not a `String`!


# Third attempt: Union types ... or not?

So far, in our model, a vehicle (at some point) may **not** have a license plate, and in that case the variable `license_plate` would have the value `nil` (which, again, its type is `Nil`). 

Having this in mind we could try to fix our model using [Union types](https://crystal-lang.org/reference/syntax_and_semantics/union_types.html), like this:


```crystal
class Vehicle
  property license_plate : String | Nil
end
```

But wait ... before trying this, we should **listen to what the compiler was saying**! Let's read it again:

>Error: instance variable **'@license_plate' of Vehicle was not initialized directly in all of the 'initialize' methods**, rendering it nilable. Indirect initialization is not supported.

It's saying more than what we think.

Let's focus on the **initialization of a vehicle**. In our model, is it correct to have a vehicle without a license plate? Nop, the problem says: 

_Each vehicle will have an unique license plate (for example ABC123). The license plate will be the way to identify each one of them._

With our model we can create **vehicles (objects) that do not have a license plate (i.e. they are not complete)** But, on the other hand (for the model to be **correct**) the `vehicle` should have a valid `license plate` since the very beginning of its existence. 

# Fourth attempt: fixing the model

Let's try the following approach:

```crystal
class Vehicle
  property license_plate : String

  def initialize
    @license_plate = "not valid"
  end
end
```

and we could use it like this:

```crystal
# app.cr
require "vehicle.cr"

v1 = Vehicle.new
puts v1.license_plate #=> "not valid"

v2 = Vehicle.new
puts v2.license_plate #=> "not valid"
```

Now the vehicles are complete (they all have license plate) ... but also we have two vehicles with the same `"not valid"` license plate 🤦‍♂️ 

ok, this is not good at all! 

Our model generates **objects that are not correct**! Here the compiler is not saying nothing, although maybe it's laughing 🙈

# The fifth ~~element~~ attempt: fixing the fix

We are going to let _the user_ of our model to set the license plate when creating a vehicle, like this:

```crystal
class Vehicle
  property license_plate : String

  def initialize(license_plate)
    @license_plate = license_plate
  end
end
```

Let's create vehicles!

```crystal
# app.cr
require "vehicle.cr"

v1 = Vehicle.new("ABC123")
puts v1.license_plate #=> "ABC123"

v2 = Vehicle.new("FOO321")
puts v2.license_plate #=> "FOO321"
```

Great! Now, with our model, we can create vehicles defining their license plate at initialization time! Meaning that now our model generates objects that are correct and complete from the beginning!

**Note:** Nothing prevents us from creating two cars with the same license plate, but this should be taken care by another object that knows all the vehicles being created (or maybe the license plates available). A vehicle itself does not (and should not) know all the other vehicles.


Next, we are going to polish our ~~vehicle~~ model a little bit more.

# Sixth attempt: even better!

_Did you see what we did here? The number 6 is even ... no? ok, bad joke_ 🙃 

Let's continue!

First, we don't want to set a license plate once the vehicle was created. We want to set it when creating a vehicle (like we are doing now) and we only want to [get](https://crystal-lang.org/reference/syntax_and_semantics/methods_and_instance_variables.html#getters-and-setters) the license plate at any time:

```crystal
class Vehicle
  getter license_plate : String

  def initialize(license_plate)
    @license_plate = license_plate
  end
end
```

Now, if we try this:

```crystal
# app.cr
require "vehicle.cr"

v1 = Vehicle.new("ABC123")
puts v1.license_plate #=> "ABC123"

v2 = Vehicle.new("FOO321")
puts v2.license_plate #=> "FOO321"

v2.license_plate = "BAR000" # this should not work!
```

The compiler will say:

```shell-session
Error: undefined method 'license_plate=' for Vehicle
```

Great! Exactly what we want!

# Seventh and last attempt

Last (but not least) we are going to use [a shorter syntax](https://crystal-lang.org/reference/syntax_and_semantics/methods_and_instance_variables.html) for initializing the instance variable:

```crystal
class Vehicle
  getter license_plate : String

  def initialize(@license_plate)
  end
end
```

And that's it! Here is our final model!

# Farewell and see you later. Summing up

* We started with a model. 
* The compiler warned us (with an error!) about situations where a variable would have an invalid value (a nil/null value situation). 
* We fixed the model so that objects were complete and correct from the very beginning.
* We rewrite our model using shorter syntax!

(and it only took us seven steps!)

Hope you enjoyed it! Until the next Crystal adventure!😃

👏 **Thanks, thanks and thanks!!** 👏
To @bcardiff and @diegoliberman for taking the time to review this post and improve the code and text!!

**About the title**
The original title for this post was going to be _When the compiler leads you to good design_ but using the words _good_ and _design_ in the same sentence could lead to and endless discussion 😅