Skip to content

Add support for . lifting #9

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

Merged
merged 3 commits into from
Apr 24, 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
7 changes: 5 additions & 2 deletions src/DataValues.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ get(x::DataValue) = isnull(x) ? throw(DataValueException()) : x.value
get(x::DataValue{Union{}}) = throw(DataValueException())
get(x::DataValue{Union{}}, y) = y

unsafe_get(x::DataValue) = x.value
unsafe_get(x) = x
Base.unsafe_get(x::DataValue) = x.value

isnull(x::DataValue) = !x.hasvalue

Base.hasvalue(x::DataValue) = x.hasvalue

const DataValuehash_seed = UInt === UInt64 ? 0x932e0143e51d0171 : 0xe51d0171

function hash(x::DataValue, h::UInt)
Expand Down Expand Up @@ -235,4 +236,6 @@ function isless{S,T}(x::DataValue{S}, y::DataValue{T})
end
end

include("broadcast.jl")

end
31 changes: 31 additions & 0 deletions src/broadcast.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Base.Broadcast._containertype(::Type{<:DataValue}) = DataValue

Base.Broadcast.promote_containertype(::Type{Any}, ::Type{DataValue}) = DataValue
Base.Broadcast.promote_containertype(::Type{DataValue}, ::Type{Any}) = DataValue
Base.Broadcast.promote_containertype(::Type{Tuple}, ::Type{DataValue}) = Tuple
Base.Broadcast.promote_containertype(::Type{DataValue}, ::Type{Tuple}) = Tuple

Choose a reason for hiding this comment

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

Does this play well with Nullable? What should broadcasting over DataValue and Nullable do? I think currently it might throw an error?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'v added two more promote_containertype methods that handle the mixed DataValue and Nullable case, and say that it will return DataValue. Did I do this correct? Was there another concern here that you had?

Choose a reason for hiding this comment

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

👍 , this looks right now.

Base.Broadcast.promote_containertype(::Type{DataValue}, ::Type{Nullable}) = DataValue
Base.Broadcast.promote_containertype(::Type{Nullable}, ::Type{DataValue}) = DataValue

Base.Broadcast.broadcast_indices(::Type{DataValue}, A) = ()

Base.Broadcast._unsafe_get_eltype(x::DataValue) = eltype(x)

Base.Broadcast._broadcast_getindex_eltype(::Type{DataValue}, T::Type) = Type{T}
Base.Broadcast._broadcast_getindex_eltype(::Type{DataValue}, A) = typeof(A)

Base.@propagate_inbounds Base.Broadcast._broadcast_getindex(::Type{DataValue}, A, I) = A

@inline function Base.Broadcast.broadcast_c(f, ::Type{DataValue}, a...)
nonnull = all(Base.hasvalue, a)

Choose a reason for hiding this comment

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

Since this shares Base.hasvalue and Base.unsafe_get with Nullable, when mixing DataValue with Nullable the result is null if any argument is null. That does make some degree of sense to me; but if so, what's the difference between DataValue and Nullable if they're interchangeable in this way?

Copy link
Member Author

Choose a reason for hiding this comment

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

So ideally I'd like mixed cases to return a DataValue in all cases. With my latest push I'm pretty close, the following all work:

julia> DataValue(3) .+ Nullable()
DataValue{Union{}}()

julia> DataValue() .+ Nullable(4)
DataValue{Union{}}()

julia> DataValue() .+ Nullable()
DataValue{Union{}}()

The thing that doesn't work is this:

julia> DataValue(3) .+ Nullable(2)
ERROR: MethodError: no method matching +(::Int64, ::Nullable{Int64})
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:424
  +(::T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}, ::T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:32
  +(::Real, ::Complex{Bool}) at complex.jl:238
  ...
Stacktrace:
 [1] broadcast_c at C:\Users\anthoff\.julia\v0.6\DataValues\src\broadcast.jl:23 [inlined]
 [2] broadcast(::Function, ::DataValues.DataValue{Int64}, ::Nullable{Int64}) at .\broadcast.jl:434

If you have any suggestion how to make that case also work and return a DataValue, it would be great!

I'm doing all of this because I need a type that behaves like what I proposed in JuliaLang/julia#21376 for Query.jl, and I don't want to wait until this is sorted out in base...

Choose a reason for hiding this comment

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

It's because your unsafe_get is local to this module, not Base.unsafe_get. It should work if you import Base: unsafe_get.

Copy link
Member Author

Choose a reason for hiding this comment

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

Perfect, that did the trick!

S = Base.Broadcast._nullable_eltype(f, a...)
if isleaftype(S) && Base.null_safe_op(f, Base.Broadcast.maptoTuple(Base.Broadcast._unsafe_get_eltype,a...).types...)
DataValue{S}(f(map(unsafe_get, a)...), nonnull)
else
if nonnull
DataValue(f(map(unsafe_get, a)...))
else
DataValue{Base.nullable_returntype(S)}()
end
end
end