-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Change isnull field of Nullable into hasvalue #18510
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
immutable NullException <: Exception | ||
end | ||
|
||
Nullable{T}(value::T, isnull::Bool=false) = Nullable{T}(value, isnull) | ||
Nullable{T}(value::T, hasvalue::Bool=true) = Nullable{T}(value, hasvalue) | ||
Nullable() = Nullable{Union{}}() | ||
|
||
eltype{T}(::Type{Nullable{T}}) = T | ||
|
@@ -53,16 +53,15 @@ otherwise, returns `y` if provided, or throws a `NullException` if not. | |
""" | ||
@inline function get{S,T}(x::Nullable{S}, y::T) | ||
if isbits(S) | ||
ifelse(x.isnull, y, x.value) | ||
ifelse(isnull(x), y, x.value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we maybe run benchmarks to make sure any There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a bad idea in general, but I don't think we have any benchmarks for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've opened a PR to add benchmarks: JuliaCI/BaseBenchmarks.jl#24. Please suggest cases to add measurements for. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see no cause for concern in the generated code. It has not changed at all with this patch.
|
||
else | ||
x.isnull ? y : x.value | ||
isnull(x) ? y : x.value | ||
end | ||
end | ||
|
||
get(x::Nullable) = x.isnull ? throw(NullException()) : x.value | ||
|
||
isnull(x::Nullable) = x.isnull | ||
get(x::Nullable) = isnull(x) ? throw(NullException()) : x.value | ||
|
||
isnull(x::Nullable) = !x.hasvalue | ||
|
||
## Operators | ||
|
||
|
@@ -107,15 +106,15 @@ and `false` if one is null but not the other: nulls are considered equal. | |
""" | ||
@inline function isequal{S,T}(x::Nullable{S}, y::Nullable{T}) | ||
if null_safe_op(isequal, S, T) | ||
(x.isnull & y.isnull) | (!x.isnull & !y.isnull & isequal(x.value, y.value)) | ||
(isnull(x) & isnull(y)) | (!isnull(x) & !isnull(y) & isequal(x.value, y.value)) | ||
else | ||
(x.isnull & y.isnull) || (!x.isnull & !y.isnull && isequal(x.value, y.value)) | ||
(isnull(x) & isnull(y)) || (!isnull(x) & !isnull(y) && isequal(x.value, y.value)) | ||
end | ||
end | ||
|
||
isequal(x::Nullable{Union{}}, y::Nullable{Union{}}) = true | ||
isequal(x::Nullable{Union{}}, y::Nullable) = y.isnull | ||
isequal(x::Nullable, y::Nullable{Union{}}) = x.isnull | ||
isequal(x::Nullable{Union{}}, y::Nullable) = isnull(y) | ||
isequal(x::Nullable, y::Nullable{Union{}}) = isnull(x) | ||
|
||
null_safe_op{S<:NullSafeTypes, | ||
T<:NullSafeTypes}(::typeof(isless), ::Type{S}, ::Type{T}) = true | ||
|
@@ -135,22 +134,22 @@ another null. | |
@inline function isless{S,T}(x::Nullable{S}, y::Nullable{T}) | ||
# NULL values are sorted last | ||
if null_safe_op(isless, S, T) | ||
(!x.isnull & y.isnull) | (!x.isnull & !y.isnull & isless(x.value, y.value)) | ||
(!isnull(x) & isnull(y)) | (!isnull(x) & !isnull(y) & isless(x.value, y.value)) | ||
else | ||
(!x.isnull & y.isnull) || (!x.isnull & !y.isnull && isless(x.value, y.value)) | ||
(!isnull(x) & isnull(y)) || (!isnull(x) & !isnull(y) && isless(x.value, y.value)) | ||
end | ||
end | ||
|
||
isless(x::Nullable{Union{}}, y::Nullable{Union{}}) = false | ||
isless(x::Nullable{Union{}}, y::Nullable) = false | ||
isless(x::Nullable, y::Nullable{Union{}}) = !x.isnull | ||
isless(x::Nullable, y::Nullable{Union{}}) = !isnull(x) | ||
|
||
==(x::Nullable, y::Nullable) = throw(NullException()) | ||
|
||
const nullablehash_seed = UInt === UInt64 ? 0x932e0143e51d0171 : 0xe51d0171 | ||
|
||
function hash(x::Nullable, h::UInt) | ||
if x.isnull | ||
if isnull(x) | ||
return h + nullablehash_seed | ||
else | ||
return hash(x.value, h + nullablehash_seed) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
xref #18511 (comment) cc @kshyatt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tkelman Indeed the two-argument form is mostly useful for tests, where using
===
requires controlling the contents ofvalue
(cf. #16923). Maybe it can also help for performance, instead of writingifelse(hasvalue, Nullable(value), Nullable())
, I would have to check. I guess @johnmyleswhite can tell.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We make use of this for null-safe operations where we can generate the
value
andhasvalue
fields without branching.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, then it should be documented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. We should document it in such a way that it's clear that you have more freedom than you usually need and that its use is a performance optimization.