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

RFC: Deprecate defaulting to scalar in broadcast #26212

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@ Deprecated or removed
* `map` on dictionaries previously operated on `key=>value` pairs. This behavior is deprecated,
and in the future `map` will operate only on values ([#5794]).

* Previously, broadcast defaulted to treating its arguments as scalars if they were not arrays
or had specifically implemented a custom broadcasting implementation. This behavior is
deprecated, and in the future `broadcast` will default to `collect`-ing these arguments ([#26212]).

* Automatically broadcasted `+` and `-` for `array + scalar`, `scalar - array`, and so-on have
been deprecated due to inconsistency with linear algebra. Use `.+` and `.-` for these operations
instead ([#22880], [#22932]).
Expand Down
31 changes: 21 additions & 10 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,14 @@ BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}()

struct Unknown <: BroadcastStyle end
BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution
BroadcastStyle(::Type) = Unknown()

"""
`Broadcast.Scalar()` is a [`BroadcastStyle`](@ref) indicating that an object is not
treated as a container for the purposes of broadcasting. This is the default for objects
that have not customized `BroadcastStyle`.
treated as a container for the purposes of broadcasting.
"""
struct Scalar <: BroadcastStyle end
BroadcastStyle(::Type) = Scalar()
BroadcastStyle(::Type{<:Ptr}) = Scalar()
BroadcastStyle(::Type{<:Union{Number,Symbol,String,Type,Function,Uninitialized,RoundingMode,Nothing,Missing}}) = Scalar()

"""
`Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style
Expand Down Expand Up @@ -366,7 +365,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}, A, I) = A
Base.@propagate_inbounds _broadcast_getindex(::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 @@ -432,6 +431,12 @@ end
end
end

collect_unknowns() = ()
@inline collect_unknowns(x, rest...) = (collect_unknown(x), collect_unknowns(rest...)...)
@inline collect_unknown(x) = collect_unknown(BroadcastStyle(typeof(x)), x)
collect_unknown(::Any, x) = x
collect_unknown(::Unknown, x) = collect(x)

"""
broadcast!(f, dest, As...)

Expand All @@ -441,7 +446,10 @@ Note that `dest` is only used to store the result, and does not supply
arguments to `f` unless it is also listed in the `As`,
as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`.
"""
@inline broadcast!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N} = broadcast!(f, dest, combine_styles(As...), As...)
@inline function broadcast!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N}
As′ = collect_unknowns(As...)
broadcast!(f, dest, combine_styles(As′...), As′...)
end
@inline broadcast!(f::Tf, dest, ::BroadcastStyle, As::Vararg{Any,N}) where {Tf,N} = broadcast!(f, dest, nothing, As...)

# Default behavior (separated out so that it can be called by users who want to extend broadcast!).
Expand Down Expand Up @@ -529,7 +537,7 @@ 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}, A) = typeof(A)
_broadcast_getindex_eltype(::Scalar, A) = typeof(A)
_broadcast_getindex_eltype(::BroadcastStyle, A) = eltype(A) # Tuple, Array, etc.

# Inferred eltype of result of broadcast(f, xs...)
Expand Down Expand Up @@ -611,8 +619,11 @@ julia> string.(("one","two","three","four"), ": ", 1:4)

```
"""
@inline broadcast(f, A, Bs...) =
broadcast(f, combine_styles(A, Bs...), nothing, nothing, A, Bs...)
@inline function broadcast(f, A, Bs...)
A′ = collect_unknown(A)
Bs′ = collect_unknowns(Bs...)
broadcast(f, combine_styles(A′, Bs′...), nothing, nothing, A′, Bs′...)
end

@inline broadcast(f, s::BroadcastStyle, ::Nothing, ::Nothing, A, Bs...) =
broadcast(f, s, combine_eltypes(f, A, Bs...), combine_indices(A, Bs...),
Expand Down Expand Up @@ -655,7 +666,7 @@ function broadcast_nonleaf(f, s::NonleafHandlingTypes, ::Type{ElType}, shape::In
_broadcast!(f, dest, keeps, Idefaults, As, Val(nargs), iter, st, 1)
end

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

@inline broadcast(f, ::Style{Tuple}, ::Nothing, ::Nothing, A, Bs...) =
tuplebroadcast(f, longest_tuple(A, Bs...), A, Bs...)
Expand Down
10 changes: 10 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,16 @@ end
# PR #23332
@deprecate ^(x, p::Integer) Base.power_by_squaring(x,p)

# Deprecate defaulting to broadcast unknown values as scalars
function Broadcast.collect_unknown(::Broadcast.Unknown, x)
depwarn("treating x::$(typeof(x)) as a scalar in broadcast is deprecated, use `Ref(x)` instead", (:broadcast, :broadcast!))
return Ref(x)
end
function Broadcast.collect_unknown(::Broadcast.Unknown, x::Union{AbstractArray,Ref})
depwarn("treating x::$(typeof(x)) as a scalar in broadcast is deprecated, use `fill(x)` instead", (:broadcast, :broadcast!))
return fill(x)
end

# Issue #25979
# The `remove_destination` keyword to `cp`, `mv`, and the unexported `cptree` has been
# renamed to `force`. To remove this deprecation, remove the `remove_destination` keyword
Expand Down
3 changes: 3 additions & 0 deletions stdlib/Dates/src/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ end
# AbstractArray{TimeType}, AbstractArray{TimeType}
(-)(x::OrdinalRange{T}, y::OrdinalRange{T}) where {T<:TimeType} = Vector(x) - Vector(y)
(-)(x::AbstractRange{T}, y::AbstractRange{T}) where {T<:TimeType} = Vector(x) - Vector(y)

# Allow dates and times to broadcast as unwrapped scalars
Base.Broadcast.BroadcastStyle(::Type{<:AbstractTime}) = Base.Broadcast.Scalar()
2 changes: 2 additions & 0 deletions stdlib/Dates/src/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ function Base.show(io::IO, df::DateFormat)
write(io, '"')
end

Base.Broadcast.BroadcastStyle(::Type{<:DateFormat}) = Base.Broadcast.Scalar()

"""
dateformat"Y-m-d H:M:S"

Expand Down
2 changes: 1 addition & 1 deletion test/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ let
end

# Ref as 0-dimensional array for broadcast
@test (-).(C_NULL, C_NULL)::UInt == 0
@test (-).(fill(C_NULL), fill(C_NULL))::UInt == fill(0)
@test (+).(1, Ref(2)) == fill(3)
@test (+).(Ref(1), Ref(2)) == fill(3)
@test (+).([[0,2], [1,3]], Ref{Vector{Int}}([1,-1])) == [[1,1], [2,2]]
Expand Down