# To use or not to use promote\_op

**URL:** <https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170>\
**Category:** General Usage\
**Tags:** question\
**Created:** [February 6, 2023, 10:32pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170 "2023-02-06T22:32:56Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 6, 2023, 10:32pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/1 "2023-02-06T22:32:56Z")

</div>

According to [Output type](https://docs.julialang.org/en/v1/manual/methods/#Output-type-computation) in the manual I should use `promote_op` to decide container types. But according to the output of `help?> Base.promote_op` I should avoid promote\_op as it is “fragile”. And if I did use promote\_op is the resulting function performant or do I need to define some new inner functions (for function barriers)?

Another style is `function foo( T, cont1, cont2 )...`, or, should I define it as `function foo( ::Val{T}, cont1, cont2 ) where{T}...`?

I have read the “Performance Tips” section multiple times as well as “Design Patterns with Parametric methods” but I am still quite confused.  
Thanks for any advice.  
–shiv–

---

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 6, 2023, 11:16pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/2 "2023-02-06T23:16:16Z")

</div>

Using `@code_warntype` it seems a good way to organize the code is as follows:

```julia
function foo( T, x, y )
 acc = zero( T )
 for i in 1:length(x)
  acc += T( x[i] * y[i] )
 end
 return acc
end

```

Here I pass the output type explicitly as the first argument. In case the user “messes up” and passes a lesser type than what `promote_op` would have chosen it is good to explicitly convert the summand to `acc`, hoping that LLVM will discard unneeded type conversions as no-ops. Declaring `acc :: T` only seems to insert unnecessary type assertions.

I also noticed that replacing `T` with `::Val{T}` as the first argument seemed to have no effect on code generation but makes the interface a bit more complex to use.

I am hoping that these observations will generalize to more complex situations…  
–shiv–

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [February 6, 2023, 11:18pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/3 "2023-02-06T23:18:41Z")

</div>

> [@00shiv](#):
>
> foo( ::Val{T},

I cannot comment on `promote_op`, but the above should be `foo(::Type{T},`

---

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 6, 2023, 11:20pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/4 "2023-02-06T23:20:45Z")

</div>

Could be. I just pulled `::Val{T}` from [Value Types](https://docs.julialang.org/en/v1/manual/types/#%22Value-types%22) in the manual.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [February 6, 2023, 11:25pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/5 "2023-02-06T23:25:03Z")

</div>

The `Type{T}` annotation forces type specialization, otherwise it’s equivalent to writing `foo(T::DataType, ...` which will treat all types the same, as values of type `DataType`.

It’s possible `Val{T}` can achieve something similar (never tried that), but you would need to call the function with `Val(T)`.

---

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 6, 2023, 11:30pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/6 "2023-02-06T23:30:39Z")

</div>

Following @DNF’s suggestion I also tried

```julia
function foo( ::Type{T}, x, y ) where T
 acc = zero( T )
 for i in 1:length(x)
  acc += T( x[i] * y[i] )
 end
 return acc
end

```

and looking at the output of `@code_warntype` this seems to be a better solution.

---

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 6, 2023, 11:33pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/7 "2023-02-06T23:33:55Z")

</div>

And for the sake of completeness code with `promote_op`:

```julia
function foo( x, y )
 op( a, b ) = a*b + a*b
 T = Base.promote_op( op, eltype(x), eltype(y) )
 acc = zero( T )
 for i in 1:length(x)
  acc += x[i] * y[i]
 end
 return acc
end

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [February 6, 2023, 11:36pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/8 "2023-02-06T23:36:55Z")

</div>

It could also be an alternative to peel off the first element of each array to correctly initialize the accumulator.

---

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 6, 2023, 11:45pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/9 "2023-02-06T23:45:34Z")

</div>

I avoided initializing `acc = x[1]*y[1]` mostly because my container types are quite nested and figuring out the first element is a bear.

In any case, thinking it over now, I see that it is more flexible to let the user choose the output type.

---

<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:** [February 7, 2023, 9:13am UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/10 "2023-02-07T09:13:52Z")

</div>

`promote_op` is used in many places in Base, so lots of functions rely on it already.  
If specifying the resulting type manually is sometimes (but not always) useful, you can nicely do

```julia
foo(x, y) = foo(promote_op(*, eltype(x), eltype(y)), x, y) # determine T automatically
foo(::Type{T}, x, y) = ... use T type ...

```

---

<div class="post-metadata">

**Author:** ![00shiv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/00shiv/32/8903_2.png) [@00shiv](https://discourse.julialang.org/u/00shiv)\
**Post date:** [February 7, 2023, 2:08pm UTC](https://discourse.julialang.org/t/to-use-or-not-to-use-promote-op/94170/11 "2023-02-07T14:08:36Z")

</div>

Oh that is a fantastic suggestion! Thanks.
