Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@something: a short-circuiting something #40729

Merged
merged 3 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,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