Unfortunately, it doesn’t make sense to me. There are a few examples of use of the Ref type, but I hardly see any value of the use of Ref from these examples.
Let’s look at this one:
julia> isa.(Ref([1,2,3]), [Array, Dict, Int]) # Treat reference values as scalar during broadcasting
I tested it, and then I tested the same statement without the Ref, like that:
isa.([1,2,3], [Array, Dict, Int])
And the result seems to be exactly the same.
Can someone explain what is the purpose of the type Ref and provide an example of use that brings some value compared to not using it ?
If you need a container with just a single value, e.g. for keeping track of something global in a deep recursion, or some such thing, Ref can be useful. But you may of course use a Vector of length 1 instead.
That’s a specific case actually. Ref is generally used to let bindings (not sure if this is the right word, I mean variables, fields, elements, etc) share data, to put it colloquially. More formally, it is a mutable container; mutable types are already mutable, so it’s mostly used on immutable types. Despite the name, mutability doesn’t have a monopoly on changing data; reassigning variables e.g. x+=1 does not mutate. However, if you want multiple bindings to see a change at once, you shouldn’t reassign all of them, but provide them with a mutable object, so Ref can help:
julia> x=y=z=1
1
julia> x+=1 # reassigning one variable...
2
julia> x,y,z # ...doesn't reassign the others.
(2, 1, 1)
julia> x=y=z=Ref(1)
Base.RefValue{Int64}(1)
julia> x[] += 1 # mutate the `Ref` by reassigning its element
2
julia> x,y,z
(Base.RefValue{Int64}(2), Base.RefValue{Int64}(2), Base.RefValue{Int64}(2))
Another way to think of Ref is as a 0-dimensional array. It is an array like structure with exactly one element in it. Ref is actually an abstract type, but when used as a constructor it produces a Base.RefValue.
You can otherwise think of it as a pointer to a single element of a specified type.