How to create a `Union` value/literal?

I’d like to initialize an array of type Array{Union{Missing,Float64},2} with a Float64 value. What’s the most succinct way?

If you were initializing an Array{Float32,2} with 3.14, you would be doing

a = fill(Float32(3.14), 2, 3)

By extension, I thought

a = fill(Union{Missing,Float64}(3.14), 2, 3)

would do the job, but it results in an error.

For that matter, I don’t know how to construct a Union{Missing,Float64} value from a Missing or from a Float64.

[This clearly shows that I don’t know what Typename() is. ]

Values always have concrete types (e.g. Float64 or Missing). There is no such thing as a value with abstract type or Union type, and you can never construct an instance of an abstract or Union type. Instead, you could call the arr = Array{...}(size) constructor and then use fill!(arr, 3.14).

julia> A = Array{Union{Missing,Float64},2}(undef, 10, 10)
10×10 Matrix{Union{Missing, Float64}}:
 missing  missing  missing  missing  …  missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing  …  missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing
 missing  missing  missing  missing     missing  missing  missing  missing

julia> fill!(A, 0.0)
10×10 Matrix{Union{Missing, Float64}}:
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0
3 Likes

Slightly convoluted but works:

julia> a = missings(Float32, 2, 3) .= 3.14
2×3 Matrix{Union{Missing, Float32}}:
 3.14  3.14  3.14
 3.14  3.14  3.14
1 Like
julia> Union{Missing, Float64}[2.1 2.1; 2.1 2.1]
2×2 Matrix{Union{Missing, Float64}}:
 2.1  2.1
 2.1  2.1

This takes advantage of the syntax described in the manual here.

2 Likes

Thank you everyone for the solutions! I’ll use one or another of them in my code.

Just for fun, I’ve realized that I can use @CameronBieganek’s idea to construct a 2x3 array of Union{Missing,Float64} like this:

julia> elem = Union{Missing, Float64}[2.1] # 1-element vector
julia> a = repeat(transpose(repeat(elem,3)),2)
2×3 Matrix{Union{Missing, Float64}}:
 2.1  2.1  2.1
 2.1  2.1  2.1

With repeat, the 1-element vector looks as if it were a scalar. (The fact is, though, repeat doesn’t accept a scalar as its first element except for a character. )

@rdeits

There is no such thing as a value with abstract type or Union type, and you can never construct an instance of an abstract or Union type.

This was the heat of my question. Thank you for elucidating!

It’s interesting, though, as @CameronBieganek points out, julia offers a special syntax

elem = Union{Missing, Float64}[2.1] # one-element vector

It’s as if an instance of an abstract type were constructed.

If a special syntax to construct an abstract scalar(?) were provided, fill could be used, for example, like

a = fill(2.1::Union{Missing,Float64}, 2, 3) # or
a = fill(Union{Missing,Float64}(2.1), 2, 3) # or
. . .

Actually, that’s not special syntax, though it does look like it. Try:

julia> T = Union{Missing, Float64}
Union{Missing, Float64}

julia> @edit T[2.1]

and you’ll see that you’re just calling getindex (the function which is always called by indexing with []), which has a built-in definition for types. It’s a bit of a pun, but it’s certainly convenient.

If I wanted to do this in my own code, I’d just make a new function:

function my_fill(::Type{T}, value, dims) where {T}
   arr = Array{T}(undef, dims)
   fill!(arr, value)
end

This will be a bit faster than your repeat version, since it doesn’t waste any time constructing a 1-element vector only to throw it away.

1 Like

This is exactly how zeros, ones, trues, and falses work.

1 Like

Thanks, @rdeits and @mkitti ! Your my_fill() is of course exactly what I wanted!

That makes me wonder whether the standard fill() could be extended to take a type argument just as zeros() does:

julia> b = zeros(Union{Missing,Float64}, (2,3))
2×3 Matrix{Union{Missing, Float64}}:
 0.0  0.0  0.0
 0.0  0.0  0.0

Your my_fill() is a natural extension of zeros():

julia> function my_fill(::Type{T}, value, dims) where {T}
   arr = Array{T}(undef, dims)
   fill!(arr, value)
end
julia> a = my_fill(Union{Missing,Float64}, 2.1, (2,3))
2×3 Matrix{Union{Missing, Float64}}:
 2.1  2.1  2.1
 2.1  2.1  2.1

When I first wanted to initialize an array with a non-zero value, I discovered fill() but wondered why it doesn’t take a type as its first argument. For example, if I wanted an Int32 array, I would write:

a = zeros(Int32, (2,3)) # works
b = fill(9, (2,3)) # Woops, I get Array{Int64} !
c = fill(Int32(9), (2,3)) # works.

Your my_fill() is pleasanter to use:

b = my_fill(Int32, 9, (2,3))

This syntax was rejected since it does not allow one to specify the array type.

For whoever found this thread interesting, I’ll add a little followup.

Revisiting this thread, I’ve just realized that the missings() function
lbilli uses conforms to the pattern of zeros() etc. It’s found in the Missings package.

Unfortunately (to me), the OffsetArrays don’t extend it as it does zeros() etc.:

using Missings
using OffsetArrays
a = zeros(Float64, 2, 3) # -> Matrix{Float64}
b = zeros(Float64, 0:1, -1:1) #-> OffsetArray(::Matrix{Float64}, 0:1, -1:1)
c = missings(Float64, 2, 3) # -> Matrix{Union{Missing, Float64}}
d = missings(Float64, 0:1, -1:1) # -> Error

I’m not saying that it’s difficult to add a method to the missings() function to support the 4th example above. I’m just writing this just for information to anybody who is interested.