# Function parametric type promotion

**URL:** <https://discourse.julialang.org/t/function-parametric-type-promotion/66832>\
**Category:** New to Julia\
**Tags:** parametric-types\
**Created:** [August 23, 2021, 9:33am UTC](https://discourse.julialang.org/t/function-parametric-type-promotion/66832 "2021-08-23T09:33:20Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![blackeneth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blackeneth/32/10353_2.png) [@blackeneth](https://discourse.julialang.org/u/blackeneth)\
**Post date:** [August 23, 2021, 9:33am UTC](https://discourse.julialang.org/t/function-parametric-type-promotion/66832/1 "2021-08-23T09:33:20Z")

</div>

Say I have a function that does a floating point calculations on the two arguments to the function.

As a convenience to users, hower, additional function definitions are defined so that if a user inputs an Integer, or a Rational, it gets covered to float.

For example:

```julia
function needfloat(a::T, b::T) where {T<:Real}
    return a+b 
end 

```

Given that there are 2 arguments as input to the function, if I want to support all combinations of Float, Integer, and Rational arguments, all the combinations of those are quite long:

```julia
needlfoat(a::Int, b::Int) = needfloat(float(a), float(b))
needfloat(a::Int, b::AbstractFloat) = needfloat(float(a), b)
needfloat(a::AbstractFloat, b::Int) = needfloat(a, float(b))
needfloat(a::Rational, b::Rational) = needfloat(float(a), float(b))
needfloat(a::Rational, b::Int) = needfloat(float(a), float(b))
needfloat(a::Rational, b::AbstractFloat) = needfloat(float(a), b)
needfloat(a::Int, b::Rational) = needfloat(float(a), float(b))
needfloat(a::AbstractFloat, b::Rational) = needfloat(a, float(b))

```

**My question:** is there a more elegant way to do this? That is, doesn’t require defining the promotion of the arguments for every combination of Integer, Rational, and AbstractFloat?

---

<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:** [August 23, 2021, 10:13am UTC](https://discourse.julialang.org/t/function-parametric-type-promotion/66832/2 "2021-08-23T10:13:04Z")

</div>

Why would you need all those explicitly? `float(1.0)` just returns `1.0`, so if you have a `needfloat(a::AbstractFloat, b::AbstractFloat)` and `needfloat(a::Real, b::Real) = needfloat(float(a), float(b))`, all arguments will be converted to floating point and passed to the specialized version:

```julia
julia> needfloat(a::Real, b::Real) = needfloat(float(a), float(b))    
needfloat (generic function with 1 method)                            
                                                                      
julia> needfloat(a::AbstractFloat, b::AbstractFloat) = "float! $a, $b"
needfloat (generic function with 2 methods)                           
                                                                      
julia> needfloat(1,2.0)                                               
"float! 1.0, 2.0"                                                     
                                                                      
julia> needfloat(1//3,2.0)                                            
"float! 0.3333333333333333, 2.0"                                      

```

Also, julia already has type promotion for numerics:

[https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Promotion](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Promotion)

Unless you’re 100% sure that you require floating point inputs though, I’d leave the promotion up to the predefined promotion rules.

---

<div class="post-metadata">

**Author:** ![blackeneth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blackeneth/32/10353_2.png) [@blackeneth](https://discourse.julialang.org/u/blackeneth)\
**Post date:** [August 24, 2021, 5:35am UTC](https://discourse.julialang.org/t/function-parametric-type-promotion/66832/3 "2021-08-24T05:35:01Z")

</div>

Well, maybe that’s what I needed to know.

However, I am seeing some weirdness.

I’m working on this function to compute Owen’s T function. I call it “owent”.

```julia
function owent(h::T1,a::T1) where {T1 <: Real}
# many statements 
t = 0.031167227832798003*h + a*0.027426509708356944
return T1(t)
end

```

However, the above _toy function_ **does not** exhibit the behavior of the full, actual, function.

From the actual function:

```julia
julia> owent(1,2)
ERROR: MethodError: no method matching owent(::Int64, ::Float64)
Closest candidates are:
  owent(::T1, ::T1) where T1<:Real at c:\Users\amgough\Documents\JuliaCode\StatsFuns.jl\src\owent.jl:63
Stacktrace:
 [1] owent(h::Int64, a::Int64)
   @ StatsFuns c:\Users\amgough\Documents\JuliaCode\StatsFuns.jl\src\owent.jl:95
 [2] top-level scope
   @ REPL[38]:1

```

What? Not method matching owent(::Int64, ::Float64) ???  
Since when is “2” a Float64???

but if I do:

```julia
julia> owent(0.5,2)
ERROR: MethodError: no method matching owent(::Float64, ::Int64)
Closest candidates are:
  owent(::T1, ::T1) where T1<:Real at c:\Users\amgough\Documents\JuliaCode\StatsFuns.jl\src\owent.jl:63
Stacktrace:
 [1] top-level scope
   @ REPL[42]:1 

```

Now the “2” is interpreted as an Int64.

_The world wonders._

---

<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:** [August 24, 2021, 6:22am UTC](https://discourse.julialang.org/t/function-parametric-type-promotion/66832/4 "2021-08-24T06:22:23Z")

</div>

> [@blackeneth](#):
>
> What? Not method matching owent(::Int64, ::Float64) ???  
> Since when is “2” a Float64???

Very much sounds like you have some old function left over in your running environment that does a partial conversion. That’s what happens when you try to manually take care of all cases instead of leaning on dispatch! Try restarting julia and work from a fresh REPL session.

> [@blackeneth](#):
>
> ```julia
> function owent(h::T1,a::T1) where {T1 <: Real}
> # many statements 
> t = 0.031167227832798003*h + a*0.027426509708356944
> return T1(t)
> end
> 
> ```

On a seperate note, this function restricts `h` and `a` to be _of the same type_:

```julia
julia> function owent(h::T1,a::T1) where {T1 <: Real}           
       # many statements                                        
       t = 0.031167227832798003*h + a*0.027426509708356944      
       return T1(t)                                             
       end                                                      
owent (generic function with 1 method)                          
                                                                
julia> owent(1.0, 2)                                            
ERROR: MethodError: no method matching owent(::Float64, ::Int64)
Closest candidates are:                                         
  owent(::T1, ::T1) where T1<:Real at REPL[1]:1                 
Stacktrace:                                                     
 [1] top-level scope                                            
   @ REPL[2]:1                                                  
                                                                
julia> owent(1, 2)                                              
ERROR: InexactError: Int64(0.08602024724951189)                 
Stacktrace:                                                     
 [1] Int64                                                      
   @ ./float.jl:812 [inlined]                                   
 [2] owent(h::Int64, a::Int64)                                  
   @ Main ./REPL[1]:4                                           
 [3] top-level scope                                            
   @ REPL[3]:1                                                  

```

You get an `InexactError` here because `0.08602024724951189` is not losslessly convertible to an `Int64` in `T1(t)`.

In a fresh session:

```julia
julia> function owent(h::Real, a::Real)                   
       # many statements                                  
       t = 0.031167227832798003*h + a*0.027426509708356944
       return t # removed the unnecessary conversion
       end                                                
owent (generic function with 1 method)                    
                                                          
julia> owent(1, 2)                                        
0.08602024724951189                                       

```

Type promotion already ensures that `h` and `a` are converted to floating point numbers before doing the operation.

Unless you absolutely positively need a type parameter, you don’t have to specify anything more than absolutely necessary in the function signature. Even with both variables typed `Real` for dispatch (and nothing more!), julia will still specialize the function to each argument type of the arguments actually passed in.
