# Complex where clause in promote\_rule

**URL:** <https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156>\
**Category:** General Usage\
**Tags:** syntax\
**Created:** [April 4, 2018, 1:24pm UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156 "2018-04-04T13:24:35Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![phlavenk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phlavenk/32/1873_2.png) [@phlavenk](https://discourse.julialang.org/u/phlavenk)\
**Post date:** [April 4, 2018, 1:24pm UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/1 "2018-04-04T13:24:36Z")

</div>

I’d like to better understand what is possible to do in the where clause of a typed function.  
In my case, I’m trying to enhance fixed-point arithmetics for a project that verifies an algorithm to be implemented on an embedded HW in fixed point arithmetics.

is it possible to define a promote rule for addition to take the fixed-point with greater “precision” (amount of decimal places) like this (do some arithmetics in the where clause) ?

```julia
promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed, f1, f2} where f1>f2 = Fixed{T,f1}
promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed, f1, f2} where f1<f2 = Fixed{T,f2}

```

This syntax errors with “error in where statement”. What works correctly - but produces a very slow code (1000x degradation) is

```julia
promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed, f1, f2} = Fixed{T,f1>f2?f1:f2}

```

If I try it with “hardcoded” **not correct** version that works only for f2 \> f1 (but produces always the same type result)

```julia
promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed, f1, f2} = Fixed{T,f2}

```

it runs on full speed!

The full code snippet to test the problem:

```julia
julia> using BenchmarkTools
julia> using FixedPointNumbers
julia> import Base.convert
julia> import Base.promote_rule
julia> convert(::Type{TF}, x::Fixed{T2,f2}) where {TF <: Fixed{T1,f1},T2, f2} where {T1, f1} = Fixed{T1, f1}(x.i>>(f2-f1), 0)

julia> promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed,f1, f2} = Fixed{T,f1>f2?f1:f2}
julia> function benchmark(x,y)
           z = x
           for i = 1:1000
               z += i + x+y
           end
           z
       end
julia> a1 = convert(Fixed{Int64, 12}, 1.0)
julia> b1 = convert(Fixed{Int64, 20}, 2.0)
julia> a2 = 1.0
julia> b2 = 2.0
julia> @btime benchmark(a1,b1)
  4.627 ms (30029 allocations: 1.31 MiB)
503501.0Q43f20

julia> @btime benchmark(a2,b2)
  1.313 μs (1 allocation: 16 bytes)
503501.0

```

---

<div class="post-metadata">

**Author:** ![juthohaegeman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juthohaegeman/32/8620_2.png) [@juthohaegeman](https://discourse.julialang.org/u/juthohaegeman)\
**Post date:** [April 4, 2018, 10:05pm UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/2 "2018-04-04T22:05:29Z")

</div>

This might be a use case for the `Base.@pure` macro:

```julia
Base.@pure Base.promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed, f1, f2} = Fixed{T,f1>f2 ? f1 : f2}

```

makes e.g. `promote_type(Fixed{Int,12}, Fixed{Int,20})` type stable.

---

<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:** [April 4, 2018, 10:22pm UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/3 "2018-04-04T22:22:07Z")

</div>

Either that or something like the following (from stdlib `Broadcast`):

[https://github.com/JuliaLang/julia/blob/416305ca4a98c64684782f1d0f539a436ff540da/base/broadcast.jl#L195-L204](https://github.com/JuliaLang/julia/blob/416305ca4a98c64684782f1d0f539a436ff540da/base/broadcast.jl#L195-L204)

---

<div class="post-metadata">

**Author:** ![phlavenk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phlavenk/32/1873_2.png) [@phlavenk](https://discourse.julialang.org/u/phlavenk)\
**Post date:** [April 5, 2018, 6:07am UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/4 "2018-04-05T06:07:25Z")

</div>

The Jutho’s solution works great - finally fixed-point is beating the floating-point.  
I’ve identified also an instability in my benchmark function, corrected version is here:

```julia
function benchmark(x,y)
    z = convert(promote_type(typeof(x), typeof(y)), 1)
    for i = 1:1000
        z += i + x + y
     end
     z
end

```

Thank you very much for the help.

---

<div class="post-metadata">

**Author:** ![phlavenk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phlavenk/32/1873_2.png) [@phlavenk](https://discourse.julialang.org/u/phlavenk)\
**Post date:** [April 5, 2018, 6:17am UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/5 "2018-04-05T06:17:16Z")

</div>

Thank you for the response. I’m sorry I don’t understand the direction you are pointing me. How can I use the \_max() function in the case of the `promote_rule` definition? Could you elaborate it in a little bit more detail?  
Thank you very much for your time - these where clauses are quite difficult for me to understand - similar to C++ templates:) Is there any blog that dives into the details what operations are allowed in the where clauses?

---

<div class="post-metadata">

**Author:** ![phlavenk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phlavenk/32/1873_2.png) [@phlavenk](https://discourse.julialang.org/u/phlavenk)\
**Post date:** [April 5, 2018, 7:14am UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/6 "2018-04-05T07:14:15Z")

</div>

For my reference, discussion on Base.@pure black magick:  
[https://discourse.julialang.org/t/pure-macro/3871](https://discourse.julialang.org/t/pure-macro/3871)

---

<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:** [April 5, 2018, 7:48pm UTC](https://discourse.julialang.org/t/complex-where-clause-in-promote-rule/10156/7 "2018-04-05T19:48:14Z")

</div>

Sorry, the point I was trying to make is that you could use this `_max` function to construct your `promote_rule` method without using `@pure`. On 0.6.2:

```julia
using Compat, FixedPointNumbers

# _max from Broadcast:
_max(V1::Val{Any}, V2::Val{Any}) = Val(Any)
_max(V1::Val{Any}, V2::Val{N}) where N = Val(Any)
_max(V1::Val{N}, V2::Val{Any}) where N = Val(Any)
_max(V1::Val, V2::Val) = __max(longest(ntuple(identity, V1), ntuple(identity, V2)))
__max(::NTuple{N,Bool}) where N = Val(N)
longest(t1::Tuple, t2::Tuple) = (true, longest(Base.tail(t1), Base.tail(t2))...)
longest(::Tuple{}, t2::Tuple) = (true, longest((), Base.tail(t2))...)
longest(t1::Tuple, ::Tuple{}) = (true, longest(Base.tail(t1), ())...)
longest(::Tuple{}, ::Tuple{}) = ()

# promote_rule using _max (note the lack of @pure):
Base.promote_rule(::Type{Fixed{T,f1}}, ::Type{Fixed{T,f2}}) where {T<:Signed, f1, f2} =
    Fixed{T, _max(Val(f1), Val(f2))}

```

This lifts the computation into the type system, so that the `promote_rule` infers correctly, this time without `@pure`:

```julia
julia> @code_warntype promote_rule(Fixed{Int, 3}, Fixed{Int, 4})
Variables:
  #self# <optimized out>
  #unused#@_2 <optimized out>
  #unused#@_3 <optimized out>

Body:
  begin 
      return FixedPointNumbers.Fixed{Int64,Val{4}()}
  end::Type{FixedPointNumbers.Fixed{Int64,Val{4}()}}

```

However, there are limits on how far you can take this:

```julia
julia> @code_warntype promote_rule(Fixed{Int, 3}, Fixed{Int, 15})
Variables:
  #self# <optimized out>
  #unused#@_2 <optimized out>
  #unused#@_3 <optimized out>

Body:
  begin 
      return (Core.apply_type)(Main.Fixed, $(Expr(:static_parameter, 1)), (Main.__max)((Core._apply)(Core.tuple, (true,), (Main.longest)((Core.tuple)(2, 3)::Tuple{Int64,Int64}, $(Expr(:invoke, MethodInstance for argtail(::Int64, ::Int64, ::Vararg{Int64,N} where N), :(Base.argtail), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)))::Tuple{Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Vararg{Any,N} where N})::Tuple{Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Bool,Vararg{Any,N} where N})::Val{_} where _)::Type{FixedPointNumbers.Fixed{Int64,_}} where _
  end::Type{FixedPointNumbers.Fixed{Int64,_}} where _

```

I was mainly trying to point out that this kind of thing is possible, not that it’s the best solution in this case. In any case, don’t use `Broadcast._max` directly, as it’s an implementation detail that may be changed without warning in a later version.

Edit (man I edit my posts a lot): realized that the result should be of the form `Fixed{Int, 4}`, not `Fixed{Int, Val(4)}`, but the idea remains the same.
