Julia generics function type parameter

Viewed 1716

I defined a function as follows:

function approx_pi(n)
    tot = Float64(0.0)
    for i in 1:n
        x = rand()
        y = rand()
        if x^2 + y^2 < 1
            tot+=1
        end
    end
    tot / n * 4
end

println(approx_pi(100_000_000))

I would like to use the same function but return a Float128 instead:

using Quadmath

function approx_pi(n)
    tot = Float128(0.0)
    for i in 1:n
        x = rand()
        y = rand()
        if x^2 + y^2 < 1
            tot+=1
        end
    end
    tot / n * 4
end

println(approx_pi(100_000_000))

I assume there is a way to achieve that through the equivalent of C# or Java generics:

function approx_pi{T}(n)
    ...
end

println(approx_pi{Float128}(100_000_000))

I could not figure out how to achieve this.

3 Answers

Types are first-class citizens in Julia, so that you can use them as function arguments in the same way you would use any other value.

For example in this case, you could simply specify the desired type as an additional argument:

julia> function approx_pi(T, n)
           tot = zero(T)   # Better than T(0)
           for i in 1:n
               x = rand(T) # Not sure whether you want these to be of
               y = rand(T) # type T, or remain as Float64
               if x^2 + y^2 < 1
                   tot+=1
               end
           end
           tot / n * 4
       end
approx_pi (generic function with 1 method)

julia> approx_pi(BigFloat, 1_000_000)
3.141276000000000000000000000000000000000000000000000000000000000000000000000003

As it was said, you can abuse Julia type system, but this is highly non-idiomatic and should never ever be used in practice.

struct ApproxPi{T} end

function ApproxPi{T}(n) where T
    tot = zero(T) 
    for i in 1:n
        x = rand(T)
        y = rand(T)
        if x^2 + y^2 < 1
            tot+=1
        end
    end
    tot / n * 4
end

julia> ApproxPi{Float32}(100_000)
3.14144f0

Here is a third variant, a derivative of Andrej's answer, which is, I believe, more idiomatic. Namely, instead of "abusing" the constructor function to do calculations, we just construct a polymorphic closure by an explicit type:

struct ApproxPi{T} end

function (::ApproxPi{T})(n) where T
    tot = zero(T) 
    for i in 1:n
        x = rand(T)
        y = rand(T)
        if x^2 + y^2 < 1
            tot += 1
        end
    end
    tot / n * 4
end

const approx_pi = ApproxPi{Float32}()

julia> approx_pi(100_000)
3.14144f0

Which should be still practical if you are not changing the types dynamically all the time.

Related