Skip to content

Commit

Permalink
@something: a short-circuiting something (#40729)
Browse files Browse the repository at this point in the history
* Create `@something`

* Create `@coalesce`

* Correction to `something`/`coalesce` docstring
  • Loading branch information
omus authored May 11, 2021
1 parent ad20f0a commit 6cebd28
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 4 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ New library functions
* Two arguments method `lock(f, lck)` now accepts a `Channel` as the second argument. ([#39312])
* New functor `Returns(value)`, which returns `value` for any arguments ([#39794])
* New macro `Base.@invoke f(arg1::T1, arg2::T2; kwargs...)` provides an easier syntax to call `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)` ([#38438])
* New macros `@something` and `@coalesce` which are short-circuiting versions of `something` and `coalesce`, respectively ([#40729])

New library features
--------------------
Expand Down
2 changes: 2 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,11 @@ export

# missing values
coalesce,
@coalesce,
ismissing,
missing,
skipmissing,
@something,
something,
isnothing,
nonmissingtype,
Expand Down
37 changes: 35 additions & 2 deletions base/missing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,12 @@ function filter(f, itr::SkipMissing{<:AbstractArray})
end

"""
coalesce(x, y...)
coalesce(x...)
Return the first value in the arguments which is not equal to [`missing`](@ref),
if any. Otherwise return `missing`.
See also [`skipmissing`](@ref), [`something`](@ref).
See also [`skipmissing`](@ref), [`something`](@ref), [`@coalesce`](@ref).
# Examples
Expand All @@ -428,3 +428,36 @@ function coalesce end
coalesce() = missing
coalesce(x::Missing, y...) = coalesce(y...)
coalesce(x::Any, y...) = x


"""
@coalesce(x...)
Short-circuiting version of [`coalesce`](@ref).
# Examples
```jldoctest
julia> f(x) = (println("f(\$x)"); missing);
julia> a = 1;
julia> a = @coalesce a f(2) f(3) error("`a` is still missing")
1
julia> b = missing;
julia> b = @coalesce b f(2) f(3) error("`b` is still missing")
f(2)
f(3)
ERROR: `b` is still missing
[...]
```
"""
macro coalesce(args...)
expr = :(missing)
for arg in reverse(args)
expr = :((val = $arg) !== missing ? val : $expr)
end
return esc(:(let val; $expr; end))
end

44 changes: 42 additions & 2 deletions base/some.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ isnothing(x) = x === nothing


"""
something(x, y...)
something(x...)
Return the first value in the arguments which is not equal to [`nothing`](@ref),
if any. Otherwise throw an error.
Arguments of type [`Some`](@ref) are unwrapped.
See also [`coalesce`](@ref), [`skipmissing`](@ref).
See also [`coalesce`](@ref), [`skipmissing`](@ref), [`@something`](@ref).
# Examples
```jldoctest
Expand All @@ -100,3 +100,43 @@ something() = throw(ArgumentError("No value arguments present"))
something(x::Nothing, y...) = something(y...)
something(x::Some, y...) = x.value
something(x::Any, y...) = x


"""
@something(x...)
Short-circuiting version of [`something`](@ref).
# Examples
```jldoctest
julia> f(x) = (println("f(\$x)"); nothing);
julia> a = 1;
julia> a = @something a f(2) f(3) error("Unable to find default for `a`")
1
julia> b = nothing;
julia> b = @something b f(2) f(3) error("Unable to find default for `b`")
f(2)
f(3)
ERROR: Unable to find default for `b`
[...]
julia> b = @something b f(2) f(3) Some(nothing)
f(2)
f(3)
julia> b === nothing
true
```
"""
macro something(args...)
expr = :(nothing)
for arg in reverse(args)
expr = :((val = $arg) !== nothing ? val : $expr)
end
return esc(:(something(let val; $expr; end)))
end

2 changes: 2 additions & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ Base.isnothing
Base.notnothing
Base.Some
Base.something
Base.@something
Base.Enums.Enum
Base.Enums.@enum
Core.Expr
Expand Down Expand Up @@ -287,6 +288,7 @@ Base.@deprecate
Base.Missing
Base.missing
Base.coalesce
Base.@coalesce
Base.ismissing
Base.skipmissing
Base.nonmissingtype
Expand Down
10 changes: 10 additions & 0 deletions test/missing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,16 @@ end
@test coalesce(missing, nothing) === nothing
end

@testset "@coalesce" begin
@test @coalesce() === missing
@test @coalesce(1) === 1
@test @coalesce(nothing) === nothing
@test @coalesce(missing) === missing

@test @coalesce(1, error("failed")) === 1
@test_throws ErrorException @coalesce(missing, error("failed"))
end

mutable struct Obj; x; end
@testset "weak references" begin
@noinline function mk_wr(r, wr)
Expand Down
10 changes: 10 additions & 0 deletions test/some.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@
@test something(missing, nothing, missing) === missing
end

@testset "@something" begin
@test_throws ArgumentError @something()
@test_throws ArgumentError @something(nothing)
@test @something(1) === 1
@test @something(Some(nothing)) === nothing

@test @something(1, error("failed")) === 1
@test_throws ErrorException @something(nothing, error("failed"))
end

# issue #26927
a = [missing, nothing, Some(nothing), Some(missing)]
@test a isa Vector{Union{Missing, Nothing, Some}}
Expand Down

0 comments on commit 6cebd28

Please sign in to comment.