# Sniffing the return type

**URL:** https://discourse.julialang.org/t/sniffing-the-return-type/83666
**Category:** General Usage
**Tags:** question
**Created:** [July 2, 2022, 3:52pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666 "2022-07-02T15:52:33Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![NicholasWMRitchie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nicholaswmritchie/32/22449_2.png) [@NicholasWMRitchie](https://discourse.julialang.org/u/NicholasWMRitchie)
#### Post date: [July 2, 2022, 3:52pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/1 "2022-07-02T15:52:33Z")

</div>

I’d like to be able to programmatically determine the return type of a closure. The closure is explicitly declared such that it has no arguments and can only possibly return one type.

Minimal example:

```julia
julia> f() = "This"

```

What I’d like would a function/macro that would do something like this:

```julia
julia> returntype(f)
String

```

I’d like to use this information at compile time to declare the type of struct member. The computation is expensive so evaluating the closure at compile time is out of the question. But presumably, the compiler can determine the return type without performing the calculation.

Is there such a thing?

---

<div class="post-metadata">

### Author: ![rikh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rikh/32/204104_2.png) [@rikh](https://discourse.julialang.org/u/rikh)
#### Post date: [July 2, 2022, 4:06pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/2 "2022-07-02T16:06:22Z")

</div>

> [@NicholasWMRitchie](#):
>
> The computation is expensive so evaluating the closure at compile time is out of the question.

I don’t know how to infer the type of `f` in your example, but what I do wonder when reading your question is: Wouldn’t a parametric struct be more suitable? For example

```julia
julia> struct A{T}
       val::T
       end

julia> f() = "This"
f (generic function with 1 method)

julia> function process()
           out = f()
           return A(out)
       end
process (generic function with 1 method)

julia> process()
A{String}("This")

```

Essentially, this determines the type of the struct during runtime instead of during compilation time. This is much more flexible. Note that it doesn’t have a negative effect on performance since the struct is still concrete.

---

<div class="post-metadata">

### Author: ![NicholasWMRitchie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nicholaswmritchie/32/22449_2.png) [@NicholasWMRitchie](https://discourse.julialang.org/u/NicholasWMRitchie)
#### Post date: [July 2, 2022, 4:21pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/3 "2022-07-02T16:21:26Z")

</div>

This is an interesting idea but I’d really like to determine the output type before evaluating the function. I’d like to create a `Ref{T}` to hold the result where the type `T` is determined by the function before the function has been evaluated.

Since posting, I might be on my way to solving this problem myself:

```julia
julia> code_typed(f)
1-element Vector{Any}:
 CodeInfo(
1 ─ return "String"
) => String
julia> code_typed(f)[1].second
String

```

---

<div class="post-metadata">

### Author: ![tchr](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tchr/32/15138_2.png) [@tchr](https://discourse.julialang.org/u/tchr)
#### Post date: [July 2, 2022, 5:06pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/4 "2022-07-02T17:06:49Z")

</div>

There’s `Core.Compiler.return_type` (but see discussions in [add docstring for return\_type by jw3126 · Pull Request #36834 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/36834) and [RFC: Public API guarantee for `Core.Compiler.return_type` by tkf · Pull Request #44340 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/44340)). That said, probably an anti-pattern to structure your code around.

---

<div class="post-metadata">

### Author: ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)
#### Post date: [July 2, 2022, 5:16pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/5 "2022-07-02T17:16:00Z")

</div>

There is also `Base.return_types`, but I don’t think it is part of the public API, so maybe dangerous to use as well.

```julia
julia> f() = "foo"
f (generic function with 1 method)

julia> Base.return_types(f, ())
1-element Vector{Any}:
 String

```

Could you perhaps explain in a bit more details the wider context? There might be other solutions…

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [July 2, 2022, 7:17pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/6 "2022-07-02T19:17:29Z")

</div>

Just to clarify some terminology here, a closure needs to capture variables, e.g. `function adder(x) y->x+y end` returns a closure. `f() = "foo"` would just be referred to as a method. Second, “declaring the type of a struct member” could be misconstrued as what you do in a `struct` definition, and that doesn’t overlap with the compile-time of a method.

It’s still not entirely clear to me what you are trying to do, but based on this

> [@NicholasWMRitchie](#):
>
> I’d like to create a `Ref{T}` to hold the result where the type `T` is determined by the function before the function has been evaluated

I’m guessing you intend to do something like this:

```julia
julia> f() = "This" # real case too expensive to be constant
f (generic function with 1 method)

julia> function g(fx::Function)
         fxtype = Base.return_types(fx)[1] # not public API
         storage = Ref{fxtype}() # unassigned Ref
         # yada yada yada
         storage[] = fx()
         return storage
       end
g (generic function with 1 method)

julia> g(f)
Base.RefValue{String}("This")

```

AFAIK there really isn’t a public API method for getting compiler-inferred return types, just code inspection methods that do it. It’s complicated because the compiler will infer abstract types for type-unstable calls e.g. `fxtype` is abstract and not the exact type of the call `fx()`. The more typical way is to just make the `Ref` right after you compute your result `storage = Ref(fx())`. In a local scope like a method body, you can even declare the `storage` variable in advance without assigning anything to it, which is as useful as an unassigned `Ref` anyway:

```julia
julia> function h(fx::Function)
         local storage # unassigned variable
         # yada yada yada
         storage = Ref(fx())
       end
h (generic function with 1 method)

julia> h(f)
Base.RefValue{String}("This")

```

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [July 2, 2022, 7:46pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/7 "2022-07-02T19:46:18Z")

</div>

Regardless of whether an internal function for this exists or not, this is fundamentally not a stable operation. All compiler stuff and inference is an optimization in the context of julia, since it’s a dynamic language. In theory, anything can happen at any point, and no return type is ever guaranteed, as type inference is allowed to fail.

---

<div class="post-metadata">

### Author: ![NicholasWMRitchie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nicholaswmritchie/32/22449_2.png) [@NicholasWMRitchie](https://discourse.julialang.org/u/NicholasWMRitchie)
#### Post date: [July 2, 2022, 9:52pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/8 "2022-07-02T21:52:08Z")

</div>

Maybe I should have said up front exactly what I’m trying to do.

I’m trying to create a lazy computation data type like this:

```julia
struct Deferred{T}
    func::Function
    item::Base.RefValue{T}

    Deferred(f::Function, ::Type{T}=Any) where {T<:Any} = new{T}(f, Base.RefValue{T}())
end

function (pc::Deferred{T})()::T where {T<:Any}
    if !isassigned(pc.item)
        pc.item[] = pc.func()
    end
    return pc.item[]
end

```

which can be used like this:

```julia
julia> df = Deferred(String) do 
            sleep(1.0) # placeholder for some expensive computation
            "Fish or fowl"
        end
julia> df()

```

Except I thought it would be nice to not have to specify the type in the constructor but rather have it determined by the function’s (or closure’s) return type.

This data type would be useful for me because it would allow me to defer calculation of an expensive member of a computation result until (if ever) it is required. (`Deferred{T}` is intended to be used as a field within an encompassing `struct`.)

It sounds like there is a non-public API to access the return type either  
`Base.return_types(fx)[1]`  
or  
` Core.Compiler.return_type(fx)`  
but using these hidden methods is discouraged.

A more complete example of how it could be used:

```julia
struct Demo{T, U}
    item1::Deferred{T}
    item2::Deferred{U}
    function Demo(d1::Deferred{V}, d2::Deferred{W}) where { V <: Any, W <: Any } 
        new{V,W}(d1,d2)
    end
end

fn(n) = n ∈ (0, 1) ? 1 : fn(n-2) + fn(n-1) # slow!

n, str = 42, "It's a bird!"
dd = Demo(Deferred(String) do 
        Base.sleep(2)
        str
    end, 
    Deferred(Int) do 
        fn(n)
    end
)

@test !isassigned(dd.item1)
@test dd.item1() == "It's a bird!"
@test isassigned(dd.item1)
@test !isassigned(dd.item2)
@test dd.item2() == 433494437
@test isassigned(dd.item2)

```

I’d like to eliminate the `String` and `Int` in the `Deferred` constructor since they are redundant.

---

<div class="post-metadata">

### Author: ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)
#### Post date: [July 2, 2022, 11:33pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/9 "2022-07-02T23:33:39Z")

</div>

This is similar to how `Base.broadcast` works - it has to allocate arrays with known eltype before the functions are run. In DiskArrays.jl we actually just defer running them at all unless you have to, somewhat how you are here - but the unmaterialised array still has the right eltype.

Broadcast uses `Base._return_type` which seems slightly safer than the `Core` method but is marked as internal with the leading underscore.

> <https://github.com/JuliaLang/julia/blob/938da264a2e40ba00653ae6659221f5af021c85b/base/broadcast.jl#L721>

But probably broadcast will always need this method or something like it, so it’s not going away. But the interface can theoretically change at any time (at which point you would just need to edit your code, if it ever happened).

---

<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: [July 2, 2022, 11:39pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/10 "2022-07-02T23:39:31Z")

</div>

I think the most important tidbit of this discussion is:

> [@Sukera](#):
>
> […] no return type is ever guaranteed, as type inference is allowed to fail.

If your code _semantically_ depends on the inferred type, then it is brittle code that has no guarantee to keep working the same way you want when you change the Julia version in the slightest.

If you are just trying to have an optimized code, well, you are storing a `Function` inside a `struct` and calling it. You gonna already take a hit there.

If you are trying to have convenient code, without having to define the type yourself, well, if your function is really heavy (and you are ok with calling it from a `struct` field) why do not use an `Any` field and function barriers? So you do not need to rely on inference working. You _defer_ knowing the type for when Julia needs to compile the method that will work over the called value, you are already deferring the value, why not defer the knowledge of its type too?

---

<div class="post-metadata">

### Author: ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)
#### Post date: [July 2, 2022, 11:47pm UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/11 "2022-07-02T23:47:59Z")

</div>

I don’t get why he shouldn’t just try `_return_type` like Base broadcasting does, if he is prepared to risk some API instability. It will handle the `Any` fallback as well.

---

<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: [July 3, 2022, 12:05am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/12 "2022-07-03T00:05:34Z")

</div>

He can, I am not saying he can’t, but if this a possibility then his code does not _semantically_ depends on the inferred type and, as he mentioned, the evaluated functions are very heavy, so I do not see what is the advantage of using `_return_type`. Will it give him a little more performance at the risk of API instability? He is allowed to make the trade-off, I was just pointing out it seems a bad trade-off in my opinion.

---

<div class="post-metadata">

### Author: ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)
#### Post date: [July 3, 2022, 12:26am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/13 "2022-07-03T00:26:01Z")

</div>

You’re right that function in the struct field makes type stability a moot point (hint: @NicholasWMRitchie you can put it in a type parameter)

But having the right return type _is_ useful for more than just performance, e.g. it lets us use GPUs where `Any` is not an allowed return type.

---

<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: [July 3, 2022, 12:33am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/14 "2022-07-03T00:33:24Z")

</div>

True. But then, again, it seems to me that what is being sacrificed is  
just a little convenience (of not declaring the type). If the code  
depends on not defaulting to Any to work, then \_return\_type should be  
avoided, as it was mentioned it can default to Any. This is what I  
called “semantically depending”.

---

<div class="post-metadata">

### Author: ![NicholasWMRitchie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nicholaswmritchie/32/22449_2.png) [@NicholasWMRitchie](https://discourse.julialang.org/u/NicholasWMRitchie)
#### Post date: [July 3, 2022, 12:40am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/15 "2022-07-03T00:40:01Z")

</div>

Thanks to all. Your responses have been very instructive.

I’m hearing that I could use an undocumented function like `Base._return_type(...)` which could change at any time. However, there are very real risks involved and the benefit won’t be significant.

It seems to me that hard coding my belief about the return type of the function could only be less robust than asking the compiler - particularly as the compiler evolves. However, knowing this type is important for optimizing downstream code which depends upon the return type (otherwise I could just assume a return type of `Any` and be done with it.)

I’ll have to play around with the alternatives and decide which compromises I’m more willing to live with.

@Raf : what do you mean by put it in a type parameter? Do you mean to declare explicitly what I expect the return type to be as part of the template type?

---

<div class="post-metadata">

### Author: ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)
#### Post date: [July 3, 2022, 12:50am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/16 "2022-07-03T00:50:18Z")

</div>

The risks of using `Base._return_type` are very low. I’m pretty sure I had a registered package that used it somewhere at some point. Broadcasting is one of the most used functions in the language and depends on it heavily.

By put it in a type parameter I mean a free type paramter in the struct that has no field.

```julia
struct Deferred{T,F}
    item::Base.RefValue{T}

    Deferred(f::Function, ::Type{T}=Any) where {T<:Any} = new{T,f}(Base.RefValue{T}())
end

```

But only if type stability of the function is useful. You could instead just use a function barrier later on and leave things as-is.

(Although I’m not even sure how bad it is in a field to be honest, maybe not so bad)

---

<div class="post-metadata">

### Author: ![NicholasWMRitchie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nicholaswmritchie/32/22449_2.png) [@NicholasWMRitchie](https://discourse.julialang.org/u/NicholasWMRitchie)
#### Post date: [July 3, 2022, 1:13am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/17 "2022-07-03T01:13:46Z")

</div>

@Raf Thanks for the suggestion. This would never have occurred to me as an option.

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [July 3, 2022, 5:49am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/18 "2022-07-03T05:49:37Z")

</div>

The documented version of `return_type` is `Base.promote_op`. It just calls `return_type` and has the same behavior.

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [July 3, 2022, 5:52am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/19 "2022-07-03T05:52:45Z")

</div>

> [@NicholasWMRitchie](#):
>
> It seems to me that hard coding my belief about the return type of the function could only be less robust than asking the compiler - particularly as the compiler evolves.

That’s not necessarily true. As mentioned above, the compiler is very much allowed to fail inferring the return type and just say “best I can do is `Any`”, even though a human may say “it can only be XYZ, obviously”. The question is more about how certain you are that a given function will always return an object of a given type.

So, my question would be: in what context does this reliance on the inferred return type come to be (other than inference unit tests)?

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [July 3, 2022, 6:12am UTC](https://discourse.julialang.org/t/sniffing-the-return-type/83666/20 "2022-07-03T06:12:26Z")

</div>

That’s a little misleading. `promote_op` has a docstring but it’s not the documentation. In fact, the docstring warns that it’s fragile and should be avoided.

[Next page](https://discourse.julialang.org/t/sniffing-the-return-type/83666.md?page=2)
