# Why does this type inference fail?

**URL:** <https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447>\
**Category:** Offtopic\
**Created:** [December 5, 2019, 3:30pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447 "2019-12-05T15:30:38Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![fxn](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fxn/32/1128_2.png) [@fxn](https://forum.crystal-lang.org/u/fxn)\
**Post date:** [December 5, 2019, 3:30pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447/1 "2019-12-05T15:30:38Z")

</div>

One AoC puzzle =\> many questions 😀.

Today, I could not understand why in this simplified example:

```auto
class C
  def initialize(listing : String)
    @program = listing.split(',').map(&.to_i)
  end
end

C.new("1")

```

the compiler is not able to infer that `@program` is an `Array(Int32)`. My reasoning is:

- `listing` has a type restriction
- `String#split` returns an `Array(String)`
- `String#to_i` either returns `Int32` or raises `ArgumentError`

A one-liner seems to support that argument:

```auto
$ crystal eval 'p typeof("1,2".split(",").map(&.to_i))'
Array(Int32)

```

Is my reasoning above wrong in some step? Are ivars special in some sense perhaps?

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [December 5, 2019, 3:45pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447/2 "2019-12-05T15:45:02Z")

</div>

Technically, the compiler could resolve the type of this expression.

But this is not necessarily as easy as that. Complex expressions can in turn rely on other instance variables being fully typed. This could cause loop dependencies and never work. And it’s not trivial to decide whether an expression can be typed without interdependencies or not. Thus the compiler only infers type from very simple expressions and asks you to annotate in all other cases. Even if technically that wasn’t strictly necessary.

For reference: [https://crystal-lang.org/reference/syntax\_and\_semantics/type\_inference.html](https://crystal-lang.org/reference/syntax_and_semantics/type_inference.html)

---

<div class="post-metadata">

**Author:** ![fxn](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fxn/32/1128_2.png) [@fxn](https://forum.crystal-lang.org/u/fxn)\
**Post date:** [December 5, 2019, 4:07pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447/3 "2019-12-05T16:07:47Z")

</div>

Gotcha!

Since `typeof` does not evaluate its arguments, why is it able to return a type in the one-liner?

---

<div class="post-metadata">

**Author:** ![konovod](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/konovod/32/229_2.png) [@konovod](https://forum.crystal-lang.org/u/konovod)\
**Post date:** [December 5, 2019, 6:28pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447/4 "2019-12-05T18:28:38Z")

</div>

The compiler was able to do it in version 0.15:  
[https://carc.in/#/r/857b](https://carc.in/#/r/857b)

But fullscale type inference for all instance vars was too slow (especially because types of instance variables can change), so it was decided to change it  
[https://crystal-lang.org/2015/12/24/the-future-of-crystal.html](https://crystal-lang.org/2015/12/24/the-future-of-crystal.html)

Now instance variables are typed in a separate pass before other methods and local variables. So compiler don’t know about `String#split` and `String#to_i` return types at the time it is selecting type of `@program`.  
Of course in theory it is possible to solve, but in general this is not too big problem and as a bonus you get readability (ability to easily see types of all instance variables).

---

<div class="post-metadata">

**Author:** ![fxn](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fxn/32/1128_2.png) [@fxn](https://forum.crystal-lang.org/u/fxn)\
**Post date:** [December 5, 2019, 8:45pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447/5 "2019-12-05T20:45:44Z")

</div>

Ahhh, that squares. Thanks a lot for the pointer to that post.

---

<div class="post-metadata">

**Author:** ![fxn](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fxn/32/1128_2.png) [@fxn](https://forum.crystal-lang.org/u/fxn)\
**Post date:** [December 5, 2019, 8:49pm UTC](https://forum.crystal-lang.org/t/why-does-this-type-inference-fail/1447/6 "2019-12-05T20:49:55Z")

</div>

For the archives, I have reduced the example to something simpler.

This compiles:

```auto
class C
  def initialize(x : Int32)
    @x = x
  end
end

C.new(1) # fine

```

but just introduce a method call, and it does not:

```auto
class C
  def initialize(x : Int32)
    @x = x.abs
  end
end

C.new(1) # can't infer the type of instance variable '@x' of C

```
