# Alternatives to \`Base.promote\_op(op, ::Type...)\` for \`op::Type\`?

**URL:** <https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289>\
**Category:** General Usage\
**Created:** [August 8, 2017, 9:03pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289 "2017-08-08T21:03:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [August 8, 2017, 9:03pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/1 "2017-08-08T21:03:16Z")

</div>

In the package I am contributing to, I would like to use `Base.promote_op` to infer the return type of an arbitrary function given by users. For example, if I want to know the return type of `sin(x)` for `x::Int64`, I can do

```julia
julia> Base.promote_op(sin, Int64)
Float64

```

However, if the function’s name is a type name, I get a deprecation warning:

```julia
julia> VERSION
v"0.6.1-pre.0"

julia> Base.promote_type(Float64, Int64)
WARNING: promote_op(op::Type, ::Type...) is deprecated as it is no longer needed in Base. If you need its functionality, consider defining it locally.
Stacktrace:
 [1] depwarn(::String, ::Symbol) at ./deprecated.jl:70
 [2] promote_op(::Type{T} where T, ::Type{T} where T) at ./deprecated.jl:399
 [3] eval(::Module, ::Any) at ./boot.jl:235
 [4] eval_user_input(::Any, ::Base.REPL.REPLBackend) at ./REPL.jl:66
 [5] macro expansion at ./REPL.jl:97 [inlined]
 [6] (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:73
while loading no file, in expression starting on line 0
Float64

```

Here, `Float64` is used as a function (like `sin` in the earlier example) rather than a type, because in many cases type names double as converter functions. In other words, the intent of the of the above code is to know the return type of the function `Float64(x)` for `x::Int64`. Still, Julia 0.6 deprecates such usage of `promote_op`.

Then, what are the alternatives to `promote_op` that works for any functions, including the ones whose names are type names? How is this situation handled in `Base`? I tried to find examples in `Base` and also searched through issues, but had a hard time finding relevant ones.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [August 8, 2017, 9:09pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/2 "2017-08-08T21:09:40Z")

</div>

If you are willing to make the assumption that `T(x)` always yields a `T` for any type `T` (seems reasonable), then you can make your own workaround:

```julia
julia> my_promote(x, y) = Base.promote_op(x, y)
my_promote (generic function with 1 method)

julia> my_promote(::Type{T}, y) where {T} = T
my_promote (generic function with 2 methods)

julia> my_promote(sin, Int64)
Float64

julia> my_promote(Float64, Int64)
Float64

```

but I’d be interested to know if Base provides something that makes this workaround unnecessary.

---

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [August 8, 2017, 9:14pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/3 "2017-08-08T21:14:07Z")

</div>

Yes, this is basically what the deprecation warning message suggests doing. I will use this solution if there is no standard solution.

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [August 8, 2017, 9:14pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/4 "2017-08-08T21:14:08Z")

</div>

```julia
julia> Core.Inference.return_type(Float64, Tuple{Int64})
Float64

```

---

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [August 8, 2017, 9:21pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/5 "2017-08-08T21:21:08Z")

</div>

> [@tkoolen](#):
>
> julia\> Core.Inference.return\_type(Float64, Tuple{Int64})  
> Float64

I’ve considered `Core.Inference.return_type` as well, but it does not properly infer the return type when the argument type is abstract:

```julia
julia> Core.Inference.return_type(sin, Tuple{Number})
Any

```

whereas `Base.promote_op` does the job correctly:

```julia
julia> Base.promote_op(sin, Number)
Number

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [August 8, 2017, 9:24pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/6 "2017-08-08T21:24:32Z")

</div>

That’s a bit of an odd requirement, though. After all, I can always implement my own type which is `<: Number` and have `sin(::MyType) = "foo"`. I don’t think we can make any statements at all about the return type of a function over an abstract type.

```julia
julia> struct Foo <: Number
       end

julia> Base.sin(::Foo) = "hello"

julia> Base.promote_op(sin, Number)
Number

julia> sin(Foo())
"hello"

```

---

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [August 8, 2017, 9:42pm UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/7 "2017-08-08T21:42:29Z")

</div>

Good point… In fact, `promote_op` is defined as follows:

```julia
function promote_op{S}(f, ::Type{S})
    @_inline_meta
    T = _return_type(f, Tuple{_default_type(S)})
    isleaftype(S) && return isleaftype(T) ? T : Any
    return typejoin(S, T)
end

```

So, `Base.promote_op(sin, Number)` simply falls into the last line of the definition: `typejoin(S, T)`, which can be misleading as your example showed…

I think I will use `Core.Inference.return_type`. Thanks, all!

---

<div class="post-metadata">

**Author:** ![PabloZubieta](https://avatars.discourse-cdn.com/v4/letter/p/ee7513/32.png) [@PabloZubieta](https://discourse.julialang.org/u/PabloZubieta)\
**Post date:** [August 9, 2017, 12:29am UTC](https://discourse.julialang.org/t/alternatives-to-base-promote-op-op-type-for-op-type/5289/8 "2017-08-09T00:29:46Z")

</div>

The behavior of `promote_op` was initially intended for some operations between arrays and scalars and for broadcast, but we’ve gradually moved from relying on it. The general strategy in Base when we want to rely on inference for predicting, for example, the element type of an array is the following:

1. Use `Core.Inference.return_type` or related functions to get an idea of the return type `R`.
2. If `R` is concrete, go ahead, use that.
3. If that is not the case and your array is empty, then also use `R`.
4. Otherwise, you get the type of the first element `T` and construct an array of that type and define a recursive function that iterates and fills the elements of the array as long as each type `S` is a subtype of the element type of the array. If that is not the case, you convert your array to an array of type `typejoin(T, S)` and continue from where you left off (calling the recursive function with the new array) until your fill the array.

You can write a similar function to the one in step 4, and avoid using `Core.Inference.return_type` at all, except that it won’t handle the empty case.

There are other scenarios where you might want inference to help you to determine a type beforehand, e.g., you want to know the type of the field of an object you’re going to construct (`Nullable`s are an example of this), in that case `Core.Inference.return_type` might be as best as it can get.

**NOTE:** This is not an advice encouraging anyone using `Core.Inference.return_type` whenever they want. It’s not exported for a reason. But in the case it’s really needed, people should try an approach similar to the one described above if possible.
