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

Replace Nullable{T} with Union{Some{T}, Void} #23642

Merged
merged 16 commits into from
Dec 15, 2017
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
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want a similar function to the get above? it seems to be a standard pattern, which becomes a bit verbose now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as I noted above it would make sense to have a convenience function for that (not sure what it would be exactly). But it can always be added later (contrary to API breaks).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot to reread those last comments in this thread! If no good name is found before this gets merged, I think I would be in favor of using a non-exported temporary name, like _get or whatever, so that at least the pattern is captured (and easily replaced when the new name comes), and at least something is available for the code which will be written in the meantime.

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 @@ -3244,6 +3244,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"
Copy link
Member

@ararslan ararslan Nov 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was unsafe_get only used for Nullable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.


# 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