if !obj.nil?
Tags: crystal, todayilearned, ruby, javascript
Published: 2022-03-18 19:04:58

When writing code that needs to handle _nilable_ objects the resulting code can be sometimes verbose and difficult to read/follow.

In this post we will start with an example (presenting the problem), followed by a (not ideal) solution and finally present different ways that different languages (especially [Crystal](https://crystal-lang.org)) use to handle _nilable_ objects.


Let's use the following [Crystal](https://crystal-lang.org) code to illustrate:

```crystal
class IntWrapper
  getter inner_value : Int32?

  def initialize(@inner_value = nil)
  end
end

# returns an IntWrapper only if parameter is positive else it returns `nil`
def create_if_positive(n : Int32): IntWrapper?
  IntWrapper.new(n) if n > 0
  # else it will return `nil`
end

number = create_if_positive(40)

puts number.inner_value + 2
```

**Notes:** 
- _The method `create_if_positive` does not make much sense but for the purpose of the example._
- _This is not an example of good design (although maybe it's an example of bad design)_ 🙃


The compiler will return:
```terminal-session
$ Error: undefined method 'inner_value' for Nil (compile-time type is (IntWrapper | Nil))
```

And the compiler is right: `create_if_positive` may return `nil` as we specified in the return type `IntWrapper?` 

So we need to check if the returned object is `nil`:

```crystal
...

if number
  puts number.inner_value + 2
else
  puts "nil branch"
end
```

And that's it! ... wait ... what? ... the compiler is saying:

```terminal-session
$ Error: undefined method '+' for Nil (compile-time type is (Int32 | Nil))
```

oooh right! Now `number.inner_value` can be also `nil` (remember `getter inner_value : Int32?`)
Let's fix it:

```crystal
...

if !number.nil? && !number.inner_value.nil?
  puts number.inner_value + 2
else
  puts "nil branch"
end
```

Now it's fixed ... wait ...

```terminal-session
Error: undefined method '+' for Nil (compile-time type is (Int32 | Nil))
```

And also, we need to tell the compiler that `number.inner_value` cannot be `nil` inside the `if` branch because we already check on that. For that we use the [Object#not_nil!](https://crystal-lang.org/api/1.3.2/Object.html#not_nil%21-instance-method) method:

```crystal
...

if !number.inner_value? && !number.inner_value.nil?
  puts number.inner_value.not_nil! + 2
else
  puts "nil branch"
end
```

Well, it's working but I would really want to write the same thing in a **more concise and clear way**.
For example, I like the following idiom when dealing with `nil` and `if` condition: 

```crystal
if a = obj # define `a` only if `obj` is not `nil`
  puts a.inspect # => the compiler knows that `a` is not `nil`!
end
```

So let's try to go in that direction. Maybe something like this:

```crystal
if number != nil && (value = number.not_nil!.inner_value)
  puts value + 2
else
  puts "nil branch"
end
```

Again, it's working but I think we can do better (I still don't like telling the compiler that `number` is not `nil`).

What can we do? 🤔 

## Safe Navigation ⛵️

At this point Ruby's [Lonely Operator](https://thoughtbot.com/blog/ruby-safe-navigation) (aka Safe Navigation Operator) came to my mind:

```ruby
class IntWrapper
  @inner_value = nil
  
  def initialize(inner_value = nil)
    @inner_value = inner_value
  end
  
  def inner_value
    @inner_value
  end
end

# 1. `number` is `nil` (using if)
number = nil

if number && number.inner_value # using if
  puts number.inner_value + 2
else
  puts "nil branch"
end

# 2. `number` is `nil`
number = nil

value = number&.inner_value
puts value + 2 unless value.nil?  # nothing is printed

# 3. `number` is not `nil`. `inner_value` is `nil`
number = IntWrapper.new()

value = number&.inner_value
puts value + 2 unless value.nil? # nothing is printed 

# 4. `number` is not `nil`. `inner_value` is not `nil`
number = IntWrapper.new(40)

value = number&.inner_value
puts value + 2  unless value.nil? # => "42"
```

Also JavaScript's [Optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining): 

```javascript
// 0. Error
let number = null;
let value = number.inner_value; // Error: Cannot read properties of null (reading 'inner_value')

// 1. number is null
let number = null
let value = number?.inner_value;
console.log(value ? value + 2 : "value is null"); // > "value is null"

// 2. `number` is not `null`. `inner_value` is `null` 
let number = {
  inner_value: null
}
let value = number?.inner_value;
console.log(value ? value + 2 : "value is null"); // > "value is null"

// 3. `number` is not `null`. `inner_value` is not `null` 
let number = {
  inner_value: 40
}
let value = number?.inner_value;
console.log(value ? value + 2 : "value is null"); // > 42
```

## Do we have some special syntax in Crystal?
The answer is **no** 😅
But don't despair! There is something really cool. It's not syntax but a method: [Object#try ](https://crystal-lang.org/api/1.3.2/Object.html#try%28%26%29-instance-method)

So we don't need to learn some new syntax but just know how this method works. It's super simple:
> Yields self. Nil overrides this method and doesn't yield.

This means that:

```crystal
nil.try { |obj| 
  # this block does not get called!
  puts obj.size 
} 
```

and a "not-nil" object will yield `self` meaning:

```crystal
"Hello!!".try { |obj|
  # the block gets called with the object itself as the parameter.
  puts obj.size # => 7
}
```

or simpler using [short one-parameter syntax](https://crystal-lang.org/reference/syntax_and_semantics/blocks_and_procs.html#short-one-parameter-syntax) (not to be confused with the previous seen Ruby's Lonely operator!😉):

```crystal
puts nil.try &.size # => nil
```

```crystal
puts "Hello!!".try &.size # => 7
```

So in our example we can write:

```crystal
if value = number.try &.inner_value
  puts value + 2
else
  puts "nil branch"
end
```

Great! It's easy to read, right? `number` _is trying to_ `number.inner_value` and if `number` is not `nil` then `value` will be assigned with the value of `inner_value` (furthermore, in the case of `inner_value` being `nil` then the if-guard fails 🤓🎉)

The complete example (3 in 1):
1. `number` is nil
1. `number` is not `nil` and `number.inner_value` is `nil`
1. `number` is not `nil` and `number.inner_value` is not `nil`

```crystal
class IntWrapper
  getter inner_value : Int32?

  def initialize(@inner_value = nil)
  end
end

def create_if_positive(n : Int32): IntWrapper?
  IntWrapper.new(n) if n > 0
  # else it will return `nil`
end

# 1. `number` is nil

number = create_if_positive(-1)

if value = number.try &.inner_value # the condition fails
  puts value + 2
else
  puts "nil branch" # => "nil branch"
end

# 2. `number` is not `nil` and `number.inner_value` is `nil`

number = IntWrapper.new # `inner_value` will be `nil` 

if value = number.try &.inner_value # the condition fails
  puts value + 2
else
  puts "nil branch" # => "nil branch"
end

# 3. `number` is not `nil` and `number.inner_value` is not `nil`

number = create_if_positive(40)

if value = number.try &.inner_value
  puts value + 2 # => 42
else
  puts "nil branch"
end
```

You can play with the example in this [playground](https://play.crystal-lang.org/#/r/cwph)

## Farewell and see you later

We have reached the end of this _safe navigation_ journey 🤪. To recap:
* we have dealt with `nil` objects and `if` conditions.
* we reviewed Ruby's Lonely operator and JavaScript's Optional chaining.
* and finally we have learned Crystal's `Object.try` method!!

Hope you enjoyed it! 😃