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

Add support for . lifting #9

merged 3 commits into from
Apr 24, 2017

Conversation

davidanthoff
Copy link
Member

Implements #8.

@TotalVerb Would you mind taking a quick look at this? This was mindless copy-paste until things worked :)

I still need to:

  • Write tests
  • Remove all the white list lifting we no longer need due to this

Copy link

@TotalVerb TotalVerb left a comment

Choose a reason for hiding this comment

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

Generally this looks correct, except possibly a missing promote_containertype method or two. It's a little annoying that promote_containertype suffers from a problem of having to define O(n^2) methods for n distinct containertypes... that's something we should consider fixing in Base.

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.@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!

@davidanthoff
Copy link
Member Author

@TotalVerb One last question: Should I see the S::Any in the following, or is that a problem?

julia> foo() = DataValue(3) .+ DataValue(5)
foo (generic function with 1 method)

julia> @code_warntype foo()
Variables:
  #self#::#foo
  nonnull::Bool
  S::Any
  #temp#@_4::Bool
  #temp#@_5::DataValues.DataValue{Int64}

Body:
  begin
      $(Expr(:inbounds, false))
      # meta: location broadcast.jl broadcast 434
      SSAValue(2) = $(Expr(:new, DataValues.DataValue{Int64}, true, 5))
      # meta: location C:\Users\anthoff\.julia\v0.6\DataValues\src\broadcast.jl broadcast_c 20
      nonnull::Bool = (Base.and_int)(true, (Base.and_int)((Core.getfield)(SSAValue(2), :hasvalue)::Bool, true)::Bool)::Bool # line 22:
      goto 8
      8:  # line 23:
      #temp#@_5::DataValues.DataValue{Int64} = $(Expr(:new, DataValues.DataValue{Int64}, :(nonnull), :((Base.add_int)(3, (Core.getfield)(SSAValue(2), :value)::Int64)::Int64)))
      goto 13 # line 28:
      13:
      # meta: pop location
      # meta: pop location
      $(Expr(:inbounds, :pop))
      return #temp#@_5::DataValues.DataValue{Int64}
  end::DataValues.DataValue{Int64}

@TotalVerb
Copy link

TotalVerb commented Apr 24, 2017

Nope, it's not a problem. S got optimized out, so inference doesn't bother inferring it (note that it doesn't actually occur in the code). See JuliaLang/julia#20801.

@davidanthoff davidanthoff changed the title [WIP] Add support for . lifting Add support for . lifting Apr 24, 2017
@davidanthoff davidanthoff merged commit 345ccf9 into master Apr 24, 2017
@davidanthoff davidanthoff deleted the lifting branch April 24, 2017 21:25
@davidanthoff
Copy link
Member Author

@TotalVerb Thanks so much for your help with this, couldn't have done it without that!

@TotalVerb
Copy link

No problem, glad you got it working! I opened an issue here about what we can do about the number of methods needed for promote_containertype, which is not so bad here but can only get worse as more types support broadcast.

davidanthoff pushed a commit that referenced this pull request Aug 18, 2017
Needed by DataStreams.jl. This uncovered a bug in _levels!() when the input contained missing values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants