Array of random Rationals

I’d like to extend rand to make a vector of rational numbers, and I’m having difficulty defining the function. For example, here’s one of the several versions I’ve tried.

function rand(::Rational{T}, n) where T<:Integer
    println("Congratulations!!! You Won!!!")
rand(Rational{Int64}, 6)

ERROR: MethodError: no method matching rand(::Type{Rational{Int64}}, ::Int64)
You may have intended to import Base.rand

I’d also like to define a method for Rational{BigInt}.

Thanks for reading.

The main reason this doesn’t work is that you’ve defined a method which expects to be given an instance of a Rational{T}, not the type Rational{T}:

julia> function foo(::Int)
         println("I got an actual Int")
foo (generic function with 1 method)

julia> function foo(::Type{Int})
         println("I got the *type* Int")
foo (generic function with 2 methods)

julia> foo(1)
I got an actual Int

julia> foo(Int)
I got the *type* Int

It also won’t do what you’re expecting because your new rand function will shadow the existing Base.rand function. That’s why your code prints a warning:

You may have intended to import Base.rand

it’s true–you probably wanted to do:

import Base: rand

function rand(....)

I personally strongly prefer to be explicit about this, and do:

function Base.rand(....)

which makes it obvious that you’re extending Base.rand and doesn’t require you to remember whether you imported or not.

Finally, a word of warning: what you are doing is the exact definition of “type piracy”: you are defining a method of a function you don’t own (rand) for a type you don’t own (Rational). This will work, but it can interact badly with anyone else who tries to do the same thing in their code. After all, if you define rand(::Type{Rational}) and they define rand(::Type{Rational}), then which one should be called if you import both packages?

If you want to be able to construct random rationals within your own code, that’s totally fine. You just may want to create your own function rather than extending Base.rand:

my_rand(args...) = rand(args...)  # by default, `my_rand` just calls `rand`
my_rand(::Type{Rational}, ...) = ...  # but my_rand also knows about Rational

Thanks! This was driving me crazy.

This is really just so I can make vectors for benchmarking, so conflicting with other packages isn’t a concern, but that’s a really good point for future use that I’m glad you told me. I had never heard of type piracy before.

In addition to @rdeits’s excellent answer (especially note type piracy: I would recommend that you define a custom type like struct SomeRationalDistribution end and a sampler for that), note that there is a recommended way of doing this: