# Why is Julia not inferring the output type of f(x,y)=x+y?

**URL:** <https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634>\
**Category:** Performance\
**Tags:** inference, code\_warntype\
**Created:** [December 26, 2021, 3:34am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634 "2021-12-26T03:34:05Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![vedantroy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vedantroy/32/32218_2.png) [@vedantroy](https://discourse.julialang.org/u/vedantroy)\
**Post date:** [December 26, 2021, 3:34am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/1 "2021-12-26T03:34:05Z")

</div>

I’m playing around with type inference in Julia.  
I have the following simple function:

```julia
f(x,y) = x+y
function q2()
  x = Number[1.0,3]
  a = 4
  b = 2
  c = f(x[1],a)
  d = f(b,c)
  f(d,x[2])
end
@code_warntype q2()

```

Here’s the output:

```julia
MethodInstance for q2()
  from q2() in Main at In[114]:2
Arguments
  #self#::Core.Const(q2)
Locals
  d::Any
  c::Any
  b::Int64
  a::Int64
  x::Vector{Number}
Body::Any
1 ─ (x = Base.getindex(Main.Number, 1.0, 3))
│ (a = 4)
│ (b = 2)
│ %4 = Base.getindex(x, 1)::Number
│ (c = Main.f(%4, a::Core.Const(4)))
│ (d = Main.f(b::Core.Const(2), c))
│ %7 = d::Any
│ %8 = Base.getindex(x, 2)::Number
│ %9 = Main.f(%7, %8)::Any
└── return %9

```

I’m surprised that the type of `c` is `Any`. Given that `f` is adding a `Float64` and a `Int64`, I would expect `c` to be inferred as a `Float64`.

I can use `c::Float64 = f(x[1],a)` to get type inference for `c` and `d`, which is also surprising b/c if Julia can determine that `d` is a `Float64` by knowing that `c` is a `Float64`, then it can track the types that flow through `f`, but that would imply it could predict the types for `c` w/o ever having an explicit type annotation.

Even after doing `c::Float64`, the final line (`f(d,x[2])`) seems to return `Any`:

```julia
%11 = Main.f(%9, %10)::Any
return %11

```

This is surprising, b/c Julia knows the type of `%9` is `Float64` and Julia knows the type of `%10` is `Number`. And presumably `Float64` + `Number` is at the very least `Number` instead of `Any`.

Any reason why Julia’s type inference is having a hard time here?

Furthermore, `code_warntype` displays `Number` in red. Is it bad if your code has types like `Number` (instead of more concrete types like `Float64`)?

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [December 26, 2021, 3:51am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/2 "2021-12-26T03:51:16Z")

</div>

> Given that `f` is adding a `Float64` and a `Int64`

```julia
%4 = Base.getindex(x, 1)::Number

```

This is the problem. It doesn’t know that `getindex` on your `Number` array returns a `Float64`.

Replacing the array with a `Float64` array, or writing  
`c = f((x[1])::Float64,a)` will also make it type stable.

---

<div class="post-metadata">

**Author:** ![c42f](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/c42f/32/52842_2.png) [@c42f](https://discourse.julialang.org/u/c42f)\
**Post date:** [December 26, 2021, 3:53am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/3 "2021-12-26T03:53:01Z")

</div>

Julia’s type inference algorithm needs to keep compilation time “reasonably small” while also computing precise type information where it “seems likely” that this would result in fast code at runtime.

Fast code machine code can generally be produced when type inference can compute concrete types (or small `Union`s of concrete types). But when an abstract type like `Number` is encountered with many subtypes, it’s unlikely to be better to infer `Number` than to widen the type to `Any`. So type inference has just chosen to widen the type here, computing a less precise result in favor of lowering compilation time.

By the way, here’s the subtypes of `Number` defined in Base. There’s also many others in external libraries:

```julia
Number
├─ Complex
└─ Real
   ├─ AbstractFloat
   │ ├─ BigFloat
   │ ├─ Float16
   │ ├─ Float32
   │ └─ Float64
   ├─ AbstractIrrational
   │ └─ Irrational
   ├─ Integer
   │ ├─ Bool
   │ ├─ Signed
   │ │ ├─ BigInt
   │ │ ├─ Int128
   │ │ ├─ Int16
   │ │ ├─ Int32
   │ │ ├─ Int64
   │ │ └─ Int8
   │ └─ Unsigned
   │ ├─ UInt128
   │ ├─ UInt16
   │ ├─ UInt32
   │ ├─ UInt64
   │ └─ UInt8
   └─ Rational

```

> [@vedantroy](#):
>
> Is it bad if your code has types like `Number` (instead of more concrete types like `Float64` )?

In short, yes.

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [December 26, 2021, 3:55am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/4 "2021-12-26T03:55:57Z")

</div>

Const prop also doesn’t work through arrays (again, they’re a bit of a black box), but IIRC Shuhei was implementing it for even unryped fields of `mutable struct`s, so it’s conceivable that this will work in the future.

---

<div class="post-metadata">

**Author:** ![vedantroy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vedantroy/32/32218_2.png) [@vedantroy](https://discourse.julialang.org/u/vedantroy)\
**Post date:** [December 26, 2021, 4:01am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/5 "2021-12-26T04:01:37Z")

</div>

If I explicitly cast the return value to a `Float64`, like this:

```julia
f(x,y) = x+y
function q2()
  x = Number[1.0,3]
  a = 4
  b = 2
  c = f(x[1],a)
  d = f(b,c)
  Float64(f(d,x[2]))
end
@code_warntype q2()

```

The last 2 lines of the output are:

```julia
│ %9 = Main.f(%7, %8)::Any
│ %10 = Main.Float64(%9)::Any
└── return %10

```

I’m not sure if I’m reading the notation incorrectly, but it looks like this is saying `Main.Float64(%9)` returns `Any`, which seems incorrect since presumably the entire point of `Main.Float64` is to either return a `Float64` or throw an exception?

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [December 26, 2021, 4:35am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/6 "2021-12-26T04:35:20Z")

</div>

```julia
struct Foo end
(::Type{Float64})(::Foo) = "not a Float64"

```

If the `Any` is a Foo, we’d get a `String`.

This is legal (but bad), therefore inference is not allowed to inner the return type is `Float64`.  
Use `::Float64` for type asserts on variable declarations or function returns.  
These will allow inference to assume the result `Float64` (or throw).

It’s generally best to try and avoid the type instability in the first place, thereby avoiding the need to recover from one, if possible.

---

<div class="post-metadata">

**Author:** ![vedantroy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vedantroy/32/32218_2.png) [@vedantroy](https://discourse.julialang.org/u/vedantroy)\
**Post date:** [December 26, 2021, 4:43am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/7 "2021-12-26T04:43:49Z")

</div>

Thanks for the example here.

I’m a little confused by the syntax:

```julia
(::Type{Float64})(::Foo) = "not a Float64"

```

I read: [Types · The Julia Language](https://docs.julialang.org/en/v1/manual/types/)

but I can’t find any mention of this syntax

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [December 26, 2021, 5:52am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/8 "2021-12-26T05:52:05Z")

</div>

[https://docs.julialang.org/en/v1/manual/types/#man-typet-type](https://docs.julialang.org/en/v1/manual/types/#man-typet-type)

basically `x::T` means `x` is a variable and `typeof(x) <: T`; but what if the argument `x` you want to pass is a type to begin with? you need `x::Type{T}`, which would be matched when `x === T`

---

<div class="post-metadata">

**Author:** ![vedantroy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vedantroy/32/32218_2.png) [@vedantroy](https://discourse.julialang.org/u/vedantroy)\
**Post date:** [December 26, 2021, 5:57am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/9 "2021-12-26T05:57:06Z")

</div>

Thanks for the explanation!  
I think what I’m confused about, is that there is nothing to the left of the `::` in `::Type{Float64}`.

Or I guess, it looks like we are assigning a string to a variable, but I don’t see any variable name on the left hand side of the assignment.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [December 26, 2021, 5:58am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/10 "2021-12-26T05:58:14Z")

</div>

> [@vedantroy](#):
>
> nothing to the left of the `::` in `::Type{Float64}` .

we’re not using that variable, so we don’t need to give it a name, this is same idea to:

```julia
f(x::Vector{<:Any}) = ...
#v.s.
f(x::Vector{T}) where {T<:Any} = ...

```

if I’m not using `T` in the method definition’s body, I don’t really care to give it a name

---

<div class="post-metadata">

**Author:** ![vedantroy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vedantroy/32/32218_2.png) [@vedantroy](https://discourse.julialang.org/u/vedantroy)\
**Post date:** [December 26, 2021, 6:02am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/11 "2021-12-26T06:02:29Z")

</div>

Hm, what would the version of this look like where we were assigning to a variable?

More broadly:

- Seems like `::Type{Float64}` is a type
- I’m not sure how `::Foo` (which is also a type?) is interacting with a `Float64` given that `Foo` is a struct. Also not sure how the end result here becomes a string of all things.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [December 26, 2021, 6:27am UTC](https://discourse.julialang.org/t/why-is-julia-not-inferring-the-output-type-of-f-x-y-x-y/73634/12 "2021-12-26T06:27:55Z")

</div>

> [@vedantroy](#):
>
> `(::Type{Float64})(::Foo) = "not a Float64"`

this method is dispatched when you have a call that looks like this

```julia
a = Foo(...)
Float64(a)

```

here `a::Foo` and `Float64::Type{Float64}`
