# Infer return type from ambiguous input

**URL:** <https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396>\
**Category:** New to Julia\
**Tags:** inference\
**Created:** [October 25, 2023, 7:08pm UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396 "2023-10-25T19:08:37Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Elmo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elmo/32/17979_2.png) [@Elmo](https://discourse.julialang.org/u/Elmo)\
**Post date:** [October 25, 2023, 7:08pm UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/1 "2023-10-25T19:08:37Z")

</div>

What is the best way to help Julia’s compiler infer that the output type of the following function can only be `Union{Int64, Nothing}`?

```julia
function inferme(x::Dict{String, Any}, y::String)
    if haskey(x, y)
        return Int(x[y])
    else
        return nothing
    end
end

```

Currently `Base.return_types(inferme, (Dict{String, Any}, String)) = Any`, which make sense (I think?) because there is no guarantee that `x[y]` yields something that doesn’t break `Int(...)`. But how can I convince the compiler that it should just trust me? (In reality `x` is read from a JSON file, and its quite heterogenous, but the keys `y` that I will feed into this function will have integers)

Context: I am attempting to implement an interface where the required functions have their output types also specified (and the interface tests against this). I already know about RequiredInterfaces.jl 🙂.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 25, 2023, 7:12pm UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/2 "2023-10-25T19:12:52Z")

</div>

```julia
julia> function inferme(x::Dict{String, Any}, y::String)
           if haskey(x, y)
               return Int(x[y])::Int
           else
               return nothing
           end
       end

```

Give the compiler the guarantee.

In Julia 1.8.5:

```julia
julia> Base.return_types(inferme, (Dict{String, Any}, String))
1-element Vector{Any}:
 Union{Nothing, Int64}

```

---

<div class="post-metadata">

**Author:** ![Elmo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elmo/32/17979_2.png) [@Elmo](https://discourse.julialang.org/u/Elmo)\
**Post date:** [October 25, 2023, 7:27pm UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/3 "2023-10-25T19:27:22Z")

</div>

Totally right 🙂 Thank you very much!!

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [October 25, 2023, 9:01pm UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/4 "2023-10-25T21:01:43Z")

</div>

Just for my own learning, is there a method for `Int(x)` that does not return an `Int` or throw? Why isn’t that call sufficient guarantee?

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 26, 2023, 12:05am UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/5 "2023-10-26T00:05:10Z")

</div>

I myself do not know. But sometimes a method is overloaded so much (i.e., have so many bodies) that inference gives up. So even if all of them return `Int` the compiler can end up not inferring it.

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [October 26, 2023, 5:31am UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/6 "2023-10-26T05:31:46Z")

</div>

well `Int(3.5)` throws `InexactError` for example but I don’t think that is the point here. The problem is that not all methods of `Int` might return an `Int` for all inputs. In principle someone can overload `Int` to do something strange like `Int(str::AbstractString)=str` or so. Proving that all methods return the same type for all inputs is probably impossible. I think Julia’s inference just gives up on calls like `f(Any)` no matter what `f` is.

If we do instead:

```julia
function inferme(x::Dict{String, T}, y::String) where T
    if haskey(x, y)
        return Int(x[y])
    else
        return nothing
    end
end

```

Then we can check what inference works on:

```julia-repl
julia> Base.return_types(inferme, (Dict{String, Int}, String))
1-element Vector{Any}:
 Union{Nothing, Int64}
julia> Base.return_types(inferme, (Dict{String, String}, String)) # branch will always throw
1-element Vector{Any}:
 Nothing
julia> Base.return_types(inferme, (Dict{String, Float64}, String)) # branch sometimes throws sometimes works
1-element Vector{Any}:
 Union{Nothing, Int64}
julia> Base.return_types(inferme, (Dict{String, Real}, String)) # abstract type - give up
1-element Vector{Any}:
 Any

```

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 26, 2023, 6:38am UTC](https://discourse.julialang.org/t/infer-return-type-from-ambiguous-input/105396/7 "2023-10-26T06:38:44Z")

</div>

> [@mrufsvold](#):
>
> Just for my own learning, is there a method for `Int(x)` that does not return an `Int` or throw?

You can check this yourself, actually (`Base.return_types` is not, however, part of the public interface):

```julia-repl
julia> collect(zip(map((f -> f(Int, Tuple{Any})), (Base.return_types, methods))...))
14-element Vector{Tuple{Any, Method}}:
 (Int64, Int64(x::Float64) @ Base float.jl:959)
 (Int64, Int64(x::Float32) @ Base float.jl:959)
 (Int64, Int64(x::Float16) @ Base float.jl:959)
 (Int64, Int64(x::Ptr) @ Core boot.jl:795)
 (Int64, Int64(x::Union{Bool, Int32, Int64, UInt16, UInt32, UInt64, UInt8, Int128, Int16, Int8, UInt128}) @ Core boot.jl:785)
 (Int64, (dt::Type{<:Integer})(ip::Sockets.IPAddr) @ Sockets ~/tmp/jl/jl/julia-d38348b476/share/julia/stdlib/v1.11/Sockets/src/IPAddr.jl:11)
 (Int64, (::Type{T})(x::BigFloat) where T<:Integer @ Base.MPFR mpfr.jl:403)
 (Int64, (::Type{T})(x::Enum{T2}) where {T<:Integer, T2<:Integer} @ Base.Enums Enums.jl:19)
 (Int64, (::Type{T})(x::Rational) where T<:Integer @ Base rational.jl:127)
 (Int64, (::Type{T})(z::Complex) where T<:Real @ Base complex.jl:44)
 (Int64, (::Type{T})(x::Base.TwicePrecision) where T<:Number @ Base twiceprecision.jl:265)
 (Int64, (::Type{T})(x::T) where T<:Number @ Core boot.jl:793)
 (Int64, (::Type{T})(x::BigInt) where T<:Union{Int128, Int16, Int32, Int64, Int8} @ Base.GMP gmp.jl:378)
 (Int64, (::Type{T})(x::AbstractChar) where T<:Union{Int32, Int64} @ Base char.jl:51)

```

The above is with a fresh REPL session, loading some packages could add new methods. StaticArrays, for example, used to have an `Int` constructor that returned non-`Int` values: [make conversion of `Length` to `Int` safer by nsajko · Pull Request #1175 · JuliaArrays/StaticArrays.jl · GitHub](https://github.com/JuliaArrays/StaticArrays.jl/pull/1175)

Relevant Julia issue: [Require constructors and `convert` to return objects of stated type? · Issue #42372 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/42372)
