# Promote\_type in @generated

**URL:** https://discourse.julialang.org/t/promote-type-in-generated/47729
**Category:** General Usage
**Created:** [October 4, 2020, 5:50am UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729 "2020-10-04T05:50:06Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 4, 2020, 5:50am UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/1 "2020-10-04T05:50:06Z")

</div>

Hi,

I want to write `toggle`, a typestable version of the `q ? a : b` syntax. What I am writing now has the disadvantage of evaluating both `a` and `b` for all values of `q`. A macro might save the day, but this is not the point of my question. Here is my code:

```julia
module A
    export toggle
    @generated function toggle(cond::Bool,a::Ta,b::Tb) where{Ta,Tb}
        Tc = promote_type(Ta,Tb)
        return :(cond ? convert($Tc,a) : convert($Tc,b))
    end
end

module B
    export T
    struct T <: Real
        value :: Float64
    end
    Base.promote_rule(::Type{T},::Type{Int}) = T
end

using Main.A
using Main.B 

a = T(3.)
b = 3
@show promote_type(typeof(a),typeof(b))
@show toggle(false,a,b)

```

So I define my type, and promote rules in module B. I define `toggle` in module A, that does _not_ (and shall not) import module B. (I want `toggle` to work for any data type, without having to enumerate the modules defining them in module A).

My code yields

```julia
promote_type(typeof(a), typeof(b)) = T
toggle(false, a, b) = 3

```

So I have the promotion rule right, but toggle is generated in ignorance of it (`Tc` evaluates to `Real`)

- why is `toggle` unaware of my promote rule? This function gets generated in a context where type `T` is defined, what did I miss?
- how can I fix that?
- Besides, surely Julia has some typestable version of `q ? a : b` already?

😀

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [October 4, 2020, 9:17am UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/2 "2020-10-04T09:17:47Z")

</div>

> [@Philippe\_Maincon1](#):
>
> a typestable version of the `q ? a : b` syntax. What I am writing now has the disadvantage of evaluating both `a` and `b` for all values of `q` . A macro might save the day, but this is not the point of my question.

Since functions (including, of course, generated functions) evaluate their arguments, I don’t think this is possible to avoid.

But even if you use a macro, either you have `a` and `b` so you can take `typeof` and promote, or somehow rely on inference, which has its own complications and may not work in general.

I would suggest just calculating a type `T` and promoting the _result_. The compiler may be able to deal with the _interim_ type instability of the result just fine when applicable.

---

<div class="post-metadata">

### Author: ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)
#### Post date: [October 4, 2020, 9:45am UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/3 "2020-10-04T09:45:38Z")

</div>

Is this a variant of the “world age” problem?  
I played around a little bit with the code above (to learn) and found this:

```julia
julia> @generated function toggle(cond::Bool,a::Ta,b::Tb) where{Ta,Tb}
               Tc = my_promote_type(Ta,Tb)
               return :(cond ? convert($Tc,a) : convert($Tc,b))
           end
toggle (generic function with 1 method)

julia> struct T <: Real
               value :: Float64
           end

julia> my_promote_type(::Type{T},::Type{Int}) = T
my_promote_type (generic function with 1 method)

julia> a = T(3.)
T(3.0)

julia> b = 2
2

julia> my_promote_type(typeof(a),typeof(b))
T

julia> toggle(false,a,b)
ERROR: MethodError: no method matching my_promote_type(::Type{T}, ::Type{Int64})
The applicable method may be too new: running in world age 27830, while current world is 27833.
Closest candidates are:
  my_promote_type(::Type{T}, ::Type{Int64}) at REPL[3]:1 (method too new to be called from this world context.)
Stacktrace:
 [1] #s1#1 at .\REPL[1]:2 [inlined]
 [2] #s1#1(::Any, ::Any, ::Any, ::Any, ::Any, ::Any) at .\none:0
 [3] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any,N} where N) at .\boot.jl:527
 [4] top-level scope at REPL[7]:1

```

I didn’t check with different modules, because the original problem shows that it doesn’t work either if you just ignore modules A and B and put it just into the REPL. Just generic `promote_type` is called, the `promote_rule` definition is ignored.

So my guess is, that `promote_rule` fails over “_The applicable method may be too new_” too, but no error is shown, because the generic alternative is available.

There are several threads about this problem:  
[https://discourse.julialang.org/search?q=The%20applicable%20method%20may%20be%20too%20new%3A%20running%20in%20world%20age](https://discourse.julialang.org/search?q=The%20applicable%20method%20may%20be%20too%20new%3A%20running%20in%20world%20age)

---

<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: [October 4, 2020, 1:26pm UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/4 "2020-10-04T13:26:08Z")

</div>

The generated function docs emphasize the need for purity in the code generating the generated function – including with respect to the method tables.

In your example, there is no reason for `toggle` to be `@generated`.

---

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 4, 2020, 2:35pm UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/5 "2020-10-04T14:35:20Z")

</div>

Promoting the result is probably the way to go to solve the original problem which brought me to this opportunity to learn. Thanks Thamas.

---

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 4, 2020, 2:38pm UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/6 "2020-10-04T14:38:30Z")

</div>

It very much looks like you nailed it. I had heard about “world age” issues before, but never connected it to anything. I’ll follow the links you provide and get wise. Thank you!

---

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 4, 2020, 2:49pm UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/7 "2020-10-04T14:49:04Z")

</div>

I went for generated in order to not introduce runtime work on inferring types. The way I understand you, this is unnecessary because a) a method gets, what was the name, instantiated for each input type combination it gets called with, and b) the promote\_type operator is then a no-op. Makes sense.

I reread the doc on generated, and its warnings about “side effects”. I do not understand your remark “including with respect to the method tables”. Would you care to elaborate? Do you maybe mean that the promote\_type call may trigger a compilation, which is a side-effect, i.e. impure?

---

<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: [October 4, 2020, 3:56pm UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/8 "2020-10-04T15:56:46Z")

</div>

> [@Philippe\_Maincon1](#):
>
> I went for generated in order to not introduce runtime work on inferring types. The way I understand you, this is unnecessary because a) a method gets, what was the name, instantiated for each input type combination it gets called with, and b) the promote\_type operator is then a no-op. Makes sense.

Yes. You can look at `code_typed`:

```julia
julia> function toggle(cond::Bool, a::Ta, b::Tb) where{Ta,Tb}
               Tc = promote_type(Ta,Tb)
               cond ? convert(Tc, a) : convert(Tc, b)
       end
toggle (generic function with 1 method)

julia> @code_typed toggle(true, 1, 1f0) # sitofp == Signed Integer TO Floating Point
CodeInfo(
1 ─ goto #3 if not cond
2 ─ %2 = Base.sitofp(Float32, a)::Float32
└── return %2
3 ─ return b
) => Float32

julia> @code_typed toggle(true, 1, 1.0) # sitofp == Signed Integer TO Floating Point
CodeInfo(
1 ─ goto #3 if not cond
2 ─ %2 = Base.sitofp(Float64, a)::Float64
└── return %2
3 ─ return b
) => Float64

julia> @code_typed toggle(true, 1f0, 1.0) # fpext == Floating Point EXTend
CodeInfo(
1 ─ goto #3 if not cond
2 ─ %2 = Base.fpext(Base.Float64, a)::Float64
└── return %2
3 ─ return b
) => Float64

julia> @code_typed toggle(true, one(BigInt), one(UInt32))
CodeInfo(
1 ─ goto #3 if not cond
2 ─ return a
3 ─ %3 = Base.GMP.MPZ.set_ui::typeof(Base.GMP.MPZ.set_ui)
│ %4 = invoke %3(_4::UInt32)::BigInt
└── return %4
) => BigInt

```

The compiler has no problem figuring things like this out.

> I reread the doc on generated, and its warnings about “side effects”. I do not understand your remark “including with respect to the method tables”. Would you care to elaborate? Do you maybe mean that the promote\_type call may trigger a compilation, which is a side-effect, i.e. impure?

I meant the method table itself is global state, but now I think I may have confused this with something said about `@pure` statements, so please don’t quote me on that.  
[The documentation](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions) however mentions `hasmethod`, and also says:

> Generated functions are only permitted to call functions that were defined _before_ the definition of the generated function. (Failure to follow this may result in getting `MethodErrors` referring to functions from a future world-age.)

---

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 4, 2020, 5:16pm UTC](https://discourse.julialang.org/t/promote-type-in-generated/47729/9 "2020-10-04T17:16:44Z")

</div>

To all of you guys: thank you so much. It’s not the first time I got great help on Discourse, it’s not the first time I got help from you all. It makes a huge difference, both in how fast I learn, and in the fun of doing so. IOU.

THANK YOU TO THE TEAM THAT CREATED JULIA AND PUSHES IT TO MATURITY  
THANK YOU TO EVERYONE THAT DEVELOPS GREAT PACKAGES  
THANK YOU TO ALL OF YOU WHO TAKE TIME TO ANSWER QUESTIONS, SILLY OR HARD
