diff --git a/src/Missings.jl b/src/Missings.jl index 79566ad..c24f421 100644 --- a/src/Missings.jl +++ b/src/Missings.jl @@ -2,7 +2,7 @@ module Missings export allowmissing, disallowmissing, ismissing, missing, missings, Missing, MissingException, levels, coalesce, passmissing, nonmissingtype, - skipmissings + skipmissings, @? using Base: ismissing, missing, Missing, MissingException @@ -283,7 +283,7 @@ struct SkipMissings{V, T} others::T end -Base.@propagate_inbounds function _anymissingindex(others::Tuple{Vararg{AbstractArray}}, i) +Base.@propagate_inbounds function _anymissingindex(others::Tuple{Vararg{AbstractArray}}, i) for oth in others oth[i] === missing && return true end @@ -292,7 +292,7 @@ Base.@propagate_inbounds function _anymissingindex(others::Tuple{Vararg{Abstract end @inline function _anymissingiterate(others::Tuple, state) - for oth in others + for oth in others y = iterate(oth, state) y !== nothing && first(y) === missing && return true end @@ -303,7 +303,7 @@ end const SkipMissingsofArrays = SkipMissings{V, T} where {V <: AbstractArray, T <: Tuple{Vararg{AbstractArray}}} -function Base.show(io::IO, mime::MIME"text/plain", itr::SkipMissings{V}) where V +function Base.show(io::IO, mime::MIME"text/plain", itr::SkipMissings{V}) where V print(io, SkipMissings, '{', V, '}', '(', itr.x, ')', " comprised of " * "$(length(itr.others) + 1) iterators") end @@ -360,7 +360,7 @@ end @inline function Base.getindex(itr::SkipMissingsofArrays, i) @boundscheck checkbounds(itr.x, i) @inbounds xi = itr.x[i] - if xi === missing || @inbounds _anymissingindex(itr.others, i) + if xi === missing || @inbounds _anymissingindex(itr.others, i) throw(MissingException("the value at index $i is missing for some element")) end return xi @@ -405,9 +405,9 @@ Base.mapreduce_impl(f, op, A::SkipMissingsofArrays, ifirst::Integer, ilast::Inte A = itr.x if ifirst == ilast @inbounds a1 = A[ifirst] - if a1 === missing + if a1 === missing return nothing - elseif _anymissingindex(itr.others, ifirst) + elseif _anymissingindex(itr.others, ifirst) return nothing else return Some(Base.mapreduce_first(f, op, a1)) @@ -461,7 +461,7 @@ end Return a vector similar to the array wrapped by the given `SkipMissings` iterator but skipping all elements with a `missing` value in one of the iterators passed to `skipmissing` and elements for which `f` returns `false`. This method -only applies when all iterators passed to `skipmissings` are arrays. +only applies when all iterators passed to `skipmissings` are arrays. # Examples ``` @@ -490,4 +490,17 @@ function filter(f, itr::SkipMissingsofArrays) y end +if VERSION >= v"1.3" + include("unionmissing.jl") +else + union_macro_ex = :( + macro _(typ) + :(Union{$(esc(typ)), Missing}) + end + ) + + union_macro_ex.args[1].args[1] = :? + eval(union_macro_ex) +end + end # module diff --git a/src/unionmissing.jl b/src/unionmissing.jl new file mode 100644 index 0000000..7438d4d --- /dev/null +++ b/src/unionmissing.jl @@ -0,0 +1,28 @@ +""" + @?(ex) + +Wrap an expression `ex` into `Union{Missing, ex}`. + +Note that Julia will pass a whole expression `ex` following `@? to it`. +Therefore using parantheses to correctly specify the `ex` is sometimes required. + +```jldoctest +julia> Vector{@?Int} +Vector{Union{Missing, Int64}} (alias for Array{Union{Missing, Int64}, 1}) + +julia> Union{String, @?Int} +Union{Missing, Int64, String} + +julia> Union{@?(Int), String} +Union{Missing, Int64, String} + +julia> @?(Int)[1, 2, 3] +3-element Vector{Union{Missing, Int64}}: + 1 + 2 + 3 +``` +""" +macro var"?"(typ) + :(Union{$(esc(typ)), Missing}) +end diff --git a/test/runtests.jl b/test/runtests.jl index cd4d997..10af174 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -193,6 +193,11 @@ struct CubeRooter end @test passmissing(Int) === Missings.PassMissing{Type{Int}}(Int) @test passmissing(cuberoot) === Missings.PassMissing{CubeRooter}(cuberoot) + @testset "@?" begin + @test (@?Int) === Union{Int, Missing} + @test @?(Int)[] isa Vector{Union{Int, Missing}} + end + @testset "deprecated" begin x = [1, 2, missing, 4] y = ["a", "b", "c", missing]