Hello everyone,
I’m new there, I didn’t publish anything before. I’m sure you won’t be mad at me if I go straight to my problem for now. (: I did not open an issue on Github because I might be doing something wrong.
Let’s consider this simplified extract of my code:
def neighbours(square : Square) : Array(Square)
[
square.pos + {0, 1},
square.pos + {1, 0},
square.pos - {0, 1},
square.pos - {1, 0}
].map { |pos| find_square(pos) }
.select { |s| !s.nil? }
end
The function find_square
returns Square | Nil
, so the select
’s predicate makes sure that we filter any Nil value. Therefore this function will return Array(Square)
at runtime.
However I get the following error from the compiler:
Error: method Engine::Game#neighbours must return Array(Engine::Square) but it is returning Array(Engine::Square | Nil)
I don’t really understand. Since the expected type will be just fine at runtime, is the compiler a bit overzealous or am I doing something wrong?
Thanks for your help! (:
A
The gist of it is the type of the array returned via the #map
call is Array(Square?)
. The #select
method retains the type of the original array. So even though there won’t actually be any nil
values, the array still allows them, thus causing the error.
In this case the solution is to just use Enumerable(T) - Crystal 1.7.2 instead.
def neighbours(square : Square) : Array(Square)
[
square.pos + {0, 1},
square.pos + {1, 0},
square.pos - {0, 1},
square.pos - {1, 0}
].compact_map { |pos| find_square(pos) }
end
Welcome to the forum 
In addition to what @Blacksmoke16 said: The recommendation for compact_map
is good because that’s the most optimized method. But in order to understand what’s going on, it’s a bit clearer when you realize that compact_map {}
is equivalent to map {}.compact
. Enumerable#compact
removes the Nil
type from the collection items. #compact_map
combines the functions of #map
and #compact
because they’re often used together and it’s more performant that way.
Btw. an alternative to compact
would be select(Square)
. This variant of the #select
method receives a type as argument which is used as item type of the resulting collection.
2 Likes
Ah! I was indeed doing it wrong. Somehow my eyes refused to see that the #select
method was propagating its input type. That makes sense.
Thanks a lot for your time, gentlemen. I’ll retain the #compact_map
solution for this specific use case, but I will surely keep the #select(T)
in mind for other cases. (:
Problem solved!
A
1 Like