Skip to content

Commit

Permalink
Replace Nullable{T} with Union{T, Void} or Union{Some{T}, Void} (#23642)
Browse files Browse the repository at this point in the history
Also add coalesce() function to return first non-nothing value and unwrap Some objects. Use the notnothing() function internally where it makes sense to assert that the result is different from nothing.

Use custom MaybeValue wrapper for ProductIterator to work around a performance regression due to type instability (information about whether a value is present or not is carried separately).
  • Loading branch information
nalimilan authored Dec 15, 2017
1 parent 9315ca0 commit 5d1e1d0
Show file tree
Hide file tree
Showing 88 changed files with 995 additions and 1,994 deletions.
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,12 @@ Deprecated or removed

* `Associative` has been deprecated in favor of `AbstractDict` ([#25012]).

* `Nullable{T}` has been deprecated and moved to the Nullables package ([#23642]).
Use `Union{T, Void}` instead, or `Union{Some{T}, Void}` if `nothing` is a possible value
(i.e. `Void <: T`). `isnull(x)` can be replaced with `x === nothing`
and `unsafe_get`/`get` can be dropped or replaced with `coalesce`.
`NullException` has been removed.

Command-line option changes
---------------------------

Expand Down
50 changes: 6 additions & 44 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Broadcast
using Base.Cartesian
using Base: Indices, OneTo, linearindices, tail, to_shape,
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache,
nullable_returntype, null_safe_op, hasvalue, isoperator
isoperator
import Base: broadcast, broadcast!
export BroadcastStyle, broadcast_indices, broadcast_similar,
broadcast_getindex, broadcast_setindex!, dotview, @__dot__
Expand Down Expand Up @@ -45,7 +45,6 @@ Naturally you can specialize this for your particular `C` (e.g., `MyContainer`).
struct Style{T} <: BroadcastStyle end

BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}()
BroadcastStyle(::Type{<:Nullable}) = Style{Nullable}()

struct Unknown <: BroadcastStyle end
BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution
Expand Down Expand Up @@ -142,7 +141,6 @@ BroadcastStyle(::BroadcastStyle, ::BroadcastStyle) = Unknown()
BroadcastStyle(::Unknown, ::Unknown) = Unknown()
BroadcastStyle(::S, ::Unknown) where S<:BroadcastStyle = S()
# Precedence rules
BroadcastStyle(::Style{Nullable}, ::Scalar) = Style{Nullable}()
BroadcastStyle(::Style{Tuple}, ::Scalar) = Style{Tuple}()
BroadcastStyle(a::AbstractArrayStyle{0}, ::Style{Tuple}) = typeof(a)(Val(1))
BroadcastStyle(a::AbstractArrayStyle, ::Style{Tuple}) = a
Expand Down Expand Up @@ -223,7 +221,6 @@ broadcast_indices() = ()
broadcast_indices(::Type{T}) where T = ()
broadcast_indices(A) = broadcast_indices(combine_styles(A), A)
broadcast_indices(::Scalar, A) = ()
broadcast_indices(::Style{Nullable}, A) = ()
broadcast_indices(::Style{Tuple}, A) = (OneTo(length(A)),)
broadcast_indices(::DefaultArrayStyle{0}, A::Ref) = ()
broadcast_indices(::AbstractArrayStyle, A) = Base.axes(A)
Expand Down Expand Up @@ -375,7 +372,7 @@ end
Base.@propagate_inbounds _broadcast_getindex(::Type{T}, I) where T = T
Base.@propagate_inbounds _broadcast_getindex(A, I) = _broadcast_getindex(combine_styles(A), A, I)
Base.@propagate_inbounds _broadcast_getindex(::DefaultArrayStyle{0}, A::Ref, I) = A[]
Base.@propagate_inbounds _broadcast_getindex(::Union{Unknown,Scalar,Style{Nullable}}, A, I) = A
Base.@propagate_inbounds _broadcast_getindex(::Union{Unknown,Scalar}, A, I) = A
Base.@propagate_inbounds _broadcast_getindex(::Any, A, I) = A[I]
Base.@propagate_inbounds _broadcast_getindex(::Style{Tuple}, A::Tuple{Any}, I) = A[1]

Expand Down Expand Up @@ -510,28 +507,20 @@ maptoTuple(f, a, b...) = Tuple{f(a), maptoTuple(f, b...).types...}
# )::_broadcast_getindex_eltype(A)
_broadcast_getindex_eltype(A) = _broadcast_getindex_eltype(combine_styles(A), A)
_broadcast_getindex_eltype(::Scalar, ::Type{T}) where T = Type{T}
_broadcast_getindex_eltype(::Union{Unknown,Scalar,Style{Nullable}}, A) = typeof(A)
_broadcast_getindex_eltype(::Union{Unknown,Scalar}, A) = typeof(A)
_broadcast_getindex_eltype(::BroadcastStyle, A) = eltype(A) # Tuple, Array, etc.

# An element type satisfying for all A:
# unsafe_get(A)::unsafe_get_eltype(A)
_unsafe_get_eltype(x::Nullable) = eltype(x)
_unsafe_get_eltype(::Type{T}) where T = Type{T}
_unsafe_get_eltype(x) = typeof(x)

# Inferred eltype of result of broadcast(f, xs...)
combine_eltypes(f, A, As...) =
Base._return_type(f, maptoTuple(_broadcast_getindex_eltype, A, As...))
_nullable_eltype(f, A, As...) =
Base._return_type(f, maptoTuple(_unsafe_get_eltype, A, As...))

"""
broadcast(f, As...)
Broadcasts the arrays, tuples, `Ref`s, nullables, and/or scalars `As` to a
Broadcasts the arrays, tuples, `Ref`s and/or scalars `As` to a
container of the appropriate type and dimensions. In this context, anything
that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s), `Tuple`,
or `Nullable` is considered a scalar. The resulting container is established by
that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s) or `Tuple`
is considered a scalar. The resulting container is established by
the following rules:
- If all the arguments are scalars, it returns a scalar.
Expand All @@ -540,10 +529,6 @@ the following rules:
(expanding singleton dimensions), and treats `Ref`s as 0-dimensional arrays,
and tuples as 1-dimensional arrays.
The following additional rule applies to `Nullable` arguments: If there is at
least one `Nullable`, and all the arguments are scalars or `Nullable`, it
returns a `Nullable` treating `Nullable`s as "containers".
A special syntax exists for broadcasting: `f.(args...)` is equivalent to
`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a
single broadcast loop.
Expand Down Expand Up @@ -602,14 +587,6 @@ julia> string.(("one","two","three","four"), ": ", 1:4)
"three: 3"
"four: 4"
julia> Nullable("X") .* "Y"
Nullable{String}("XY")
julia> broadcast(/, 1.0, Nullable(2.0))
Nullable{Float64}(0.5)
julia> (1 + im) ./ Nullable{Int}()
Nullable{Complex{Float64}}()
```
"""
@inline broadcast(f, A, Bs...) =
Expand Down Expand Up @@ -656,21 +633,6 @@ function broadcast_nonleaf(f, s::NonleafHandlingTypes, ::Type{ElType}, shape::In
return _broadcast!(f, dest, keeps, Idefaults, As, Val(nargs), iter, st, 1)
end

@inline function broadcast(f, ::Style{Nullable}, ::Void, ::Void, a...)
nonnull = all(hasvalue, a)
S = _nullable_eltype(f, a...)
if Base._isleaftype(S) && null_safe_op(f, maptoTuple(_unsafe_get_eltype,
a...).types...)
Nullable{S}(f(map(unsafe_get, a)...), nonnull)
else
if nonnull
Nullable(f(map(unsafe_get, a)...))
else
Nullable{nullable_returntype(S)}()
end
end
end

broadcast(f, ::Union{Scalar,Unknown}, ::Void, ::Void, a...) = f(a...)

@inline broadcast(f, ::Style{Tuple}, ::Void, ::Void, A, Bs...) =
Expand Down
18 changes: 9 additions & 9 deletions base/channels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ Other constructors:
* `Channel(sz)`: equivalent to `Channel{Any}(sz)`
"""
mutable struct Channel{T} <: AbstractChannel
cond_take::Condition # waiting for data to become available
cond_put::Condition # waiting for a writeable slot
cond_take::Condition # waiting for data to become available
cond_put::Condition # waiting for a writeable slot
state::Symbol
excp::Nullable{Exception} # Exception to be thrown when state != :open
excp::Union{Exception, Void} # exception to be thrown when state != :open

data::Vector{T}
sz_max::Int # maximum size of channel
sz_max::Int # maximum size of channel

# Used when sz_max == 0, i.e., an unbuffered channel.
waiters::Int
Expand All @@ -42,7 +42,7 @@ mutable struct Channel{T} <: AbstractChannel
if sz < 0
throw(ArgumentError("Channel size must be either 0, a positive integer or Inf"))
end
ch = new(Condition(), Condition(), :open, Nullable{Exception}(), Vector{T}(), sz, 0)
ch = new(Condition(), Condition(), :open, nothing, Vector{T}(), sz, 0)
if sz == 0
ch.takers = Vector{Task}()
ch.putters = Vector{Task}()
Expand Down Expand Up @@ -129,7 +129,7 @@ isbuffered(c::Channel) = c.sz_max==0 ? false : true

function check_channel_state(c::Channel)
if !isopen(c)
!isnull(c.excp) && throw(get(c.excp))
c.excp !== nothing && throw(c.excp)
throw(closed_exception())
end
end
Expand All @@ -143,7 +143,7 @@ Close a channel. An exception is thrown by:
"""
function close(c::Channel)
c.state = :closed
c.excp = Nullable{}(closed_exception())
c.excp = closed_exception()
notify_error(c)
nothing
end
Expand Down Expand Up @@ -237,7 +237,7 @@ function close_chnl_on_taskdone(t::Task, ref::WeakRef)
!isopen(c) && return
if istaskfailed(t)
c.state = :closed
c.excp = Nullable{Exception}(task_result(t))
c.excp = task_result(t)
notify_error(c)
else
close(c)
Expand Down Expand Up @@ -387,7 +387,7 @@ function notify_error(c::Channel, err)
foreach(t->schedule(t, err; error=true), waiters)
end
end
notify_error(c::Channel) = notify_error(c, get(c.excp))
notify_error(c::Channel) = notify_error(c, c.excp)

eltype(::Type{Channel{T}}) where {T} = T

Expand Down
2 changes: 1 addition & 1 deletion base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ color_normal = text_colors[:normal]
function repl_color(key, default)
env_str = get(ENV, key, "")
c = tryparse(Int, env_str)
c_conv = isnull(c) ? Symbol(env_str) : get(c)
c_conv = coalesce(c, Symbol(env_str))
haskey(text_colors, c_conv) ? c_conv : default
end

Expand Down
21 changes: 13 additions & 8 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1512,8 +1512,8 @@ end
function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false)
Base.depwarn(string(
"`LibGit2.prompt(msg::AbstractString; default::AbstractString=\"\", password::Bool=false)` is deprecated, use ",
"`get(Base.prompt(msg, default=default, password=password), \"\")` instead."), :prompt)
Base.get(Base.prompt(msg, default=default, password=password), "")
"`result = Base.prompt(msg, default=default, password=password); result === nothing ? \"\" : result` instead."), :prompt)
coalesce(Base.prompt(msg, default=default, password=password), "")
end
end

Expand Down Expand Up @@ -1658,20 +1658,20 @@ import .Iterators.enumerate

# PR #23640
# when this deprecation is deleted, remove all calls to it, and replace all keywords of:
# `payload::Union{CredentialPayload,Nullable{<:Union{AbstractCredential, CachedCredentials}}}`
# `payload::Union{CredentialPayload, AbstractCredential, CachedCredentials, Void}`
# with `payload::CredentialPayload` from base/libgit2/libgit2.jl
@eval LibGit2 function deprecate_nullable_creds(f, sig, payload)
if isa(payload, Nullable{<:Union{AbstractCredential, CachedCredentials}})
if isa(payload, Union{AbstractCredential, CachedCredentials, Void})
# Note: Be careful not to show the contents of the credentials as it could reveal a
# password.
if isnull(payload)
msg = "LibGit2.$f($sig; payload=Nullable()) is deprecated, use "
if payload === nothing
msg = "LibGit2.$f($sig; payload=nothing) is deprecated, use "
msg *= "LibGit2.$f($sig; payload=LibGit2.CredentialPayload()) instead."
p = CredentialPayload()
else
cred = unsafe_get(payload)
cred = payload
C = typeof(cred)
msg = "LibGit2.$f($sig; payload=Nullable($C(...))) is deprecated, use "
msg = "LibGit2.$f($sig; payload=$C(...)) is deprecated, use "
msg *= "LibGit2.$f($sig; payload=LibGit2.CredentialPayload($C(...))) instead."
p = CredentialPayload(cred)
end
Expand Down Expand Up @@ -3254,6 +3254,11 @@ end
@deprecate indices(a) axes(a)
@deprecate indices(a, d) axes(a, d)

@deprecate_moved Nullable "Nullables"
@deprecate_moved NullException "Nullables"
@deprecate_moved isnull "Nullables"
@deprecate_moved unsafe_get "Nullables"

# END 0.7 deprecations

# BEGIN 1.0 deprecations
Expand Down
14 changes: 7 additions & 7 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ linenumber, source code, and fielddocs.
"""
mutable struct DocStr
text :: Core.SimpleVector
object :: Nullable
object :: Any
data :: Dict{Symbol, Any}
end

Expand All @@ -160,9 +160,9 @@ function docstr(binding::Binding, @nospecialize typesig = Union{})
end
docstr(object, data = Dict()) = _docstr(object, data)

_docstr(vec::Core.SimpleVector, data) = DocStr(vec, Nullable(), data)
_docstr(str::AbstractString, data) = DocStr(Core.svec(str), Nullable(), data)
_docstr(object, data) = DocStr(Core.svec(), Nullable(object), data)
_docstr(vec::Core.SimpleVector, data) = DocStr(vec, nothing, data)
_docstr(str::AbstractString, data) = DocStr(Core.svec(str), nothing, data)
_docstr(object, data) = DocStr(Core.svec(), object, data)

_docstr(doc::DocStr, data) = (doc.data = merge(data, doc.data); doc)

Expand All @@ -184,13 +184,13 @@ end
@noinline formatdoc(buffer, d, part) = print(buffer, part)

function parsedoc(d::DocStr)
if isnull(d.object)
if d.object === nothing
md = formatdoc(d)
md.meta[:module] = d.data[:module]
md.meta[:path] = d.data[:path]
d.object = Nullable(md)
d.object = md
end
get(d.object)
d.object
end

"""
Expand Down
2 changes: 1 addition & 1 deletion base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ Void
nothing
The singleton instance of type `Void`, used by convention when there is no value to return
(as in a C `void` function). Can be converted to an empty [`Nullable`](@ref) value.
(as in a C `void` function) or when a variable or field holds no value.
"""
nothing

Expand Down
8 changes: 2 additions & 6 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export
MergeSort,
Missing,
NTuple,
Nullable,
ObjectIdDict,
OrdinalRange,
Pair,
Expand All @@ -100,6 +99,7 @@ export
AbstractSerializer,
SerializationState,
Set,
Some,
StepRange,
StepRangeLen,
StridedArray,
Expand Down Expand Up @@ -150,7 +150,6 @@ export
InvalidStateException,
KeyError,
MissingException,
NullException,
ParseError,
SystemError,
StringIndexError,
Expand Down Expand Up @@ -863,6 +862,7 @@ export
fetch,

# missing values
coalesce,
ismissing,
missing,
skipmissing,
Expand Down Expand Up @@ -1145,10 +1145,6 @@ export
unsafe_store!,
unsafe_write,

# nullable types
isnull,
unsafe_get,

# Macros
# parser internal
@__FILE__,
Expand Down
10 changes: 4 additions & 6 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -234,19 +234,17 @@ convert(::Type{Signed}, x::BigInt) = x
hastypemax(::Type{BigInt}) = false

function tryparse_internal(::Type{BigInt}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool)
_n = Nullable{BigInt}()

# don't make a copy in the common case where we are parsing a whole String
bstr = startpos == start(s) && endpos == endof(s) ? String(s) : String(SubString(s,startpos,endpos))

sgn, base, i = Base.parseint_preamble(true,Int(base_),bstr,start(bstr),endof(bstr))
if !(2 <= base <= 62)
raise && throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base"))
return _n
return nothing
end
if i == 0
raise && throw(ArgumentError("premature end of integer: $(repr(bstr))"))
return _n
return nothing
end
z = BigInt()
if Base.containsnul(bstr)
Expand All @@ -256,9 +254,9 @@ function tryparse_internal(::Type{BigInt}, s::AbstractString, startpos::Int, end
end
if err != 0
raise && throw(ArgumentError("invalid BigInt: $(repr(bstr))"))
return _n
return nothing
end
Nullable(flipsign!(z, sgn))
flipsign!(z, sgn)
end

convert(::Type{BigInt}, x::Union{Clong,Int32}) = MPZ.set_si(x)
Expand Down
6 changes: 3 additions & 3 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -603,12 +603,12 @@ macro big_str(s)
print(bf, s[end])
seekstart(bf)
n = tryparse(BigInt, String(take!(bf)))
!isnull(n) && return get(n)
n === nothing || return n
else
n = tryparse(BigInt, s)
!isnull(n) && return get(n)
n === nothing || return n
n = tryparse(BigFloat, s)
!isnull(n) && return get(n)
n === nothing || return n
end
message = "invalid number format $s for BigInt or BigFloat"
return :(throw(ArgumentError($message)))
Expand Down
Loading

0 comments on commit 5d1e1d0

Please sign in to comment.