diff --git a/NEWS.md b/NEWS.md index b79436318a020..87e8e5247fd79 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,8 @@ New language features * The compiler optimization level can now be set per-module using the experimental macro `Base.Experimental.@optlevel n`. For code that is not performance-critical, setting this to 0 or 1 can provide significant latency improvements ([#34896]). +* `Some`containers now support broadcast as zero dimensional immutable containers. `Some(x)` + should be preferred to `Ref(x)` when you wish to exempt `x` from broadcasting ([#35778]). Language changes ---------------- diff --git a/base/Base.jl b/base/Base.jl index c5dd34271493d..f0217a8d9b455 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -193,6 +193,8 @@ include("broadcast.jl") using .Broadcast using .Broadcast: broadcasted, broadcasted_kwsyntax, materialize, materialize! +include("somebroadcast.jl") + # missing values include("missing.jl") diff --git a/base/broadcast.jl b/base/broadcast.jl index 168431b5566d9..3b7daae51abf2 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -668,15 +668,15 @@ julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already s 2 3 -julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars -Base.RefValue{Type{Int64}}(Int64) +julia> Broadcast.broadcastable(Int64) # Types don't support axes, indexing, or iteration but are commonly used as scalars +Some(Int64) julia> Broadcast.broadcastable("hello") # Strings break convention of matching iteration and act like a scalar instead -Base.RefValue{String}("hello") +Some("hello") ``` """ -broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,Regex,Pair}) = Ref(x) -broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T) +broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,Regex,Pair}) = Some(x) +broadcastable(::Type{T}) where {T} = Some{Type{T}}(T) broadcastable(x::Union{AbstractArray,Number,Ref,Tuple,Broadcasted}) = x # Default to collecting iterables — which will error for non-iterables broadcastable(x) = collect(x) diff --git a/base/some.jl b/base/some.jl index fbbc3aed0e284..e94bf0ebfcb1a 100644 --- a/base/some.jl +++ b/base/some.jl @@ -6,6 +6,8 @@ A wrapper type used in `Union{Some{T}, Nothing}` to distinguish between the absence of a value ([`nothing`](@ref)) and the presence of a `nothing` value (i.e. `Some(nothing)`). +`Some` is also used in broadcasting to treat its enclosed value as a scalar. + Use [`something`](@ref) to access the value wrapped by a `Some` object. """ struct Some{T} diff --git a/base/somebroadcast.jl b/base/somebroadcast.jl new file mode 100644 index 0000000000000..ed8cc296696bf --- /dev/null +++ b/base/somebroadcast.jl @@ -0,0 +1,18 @@ +#Methods some Some can broadcast +getindex(s::Some) = s.value +getindex(s::Some, ::CartesianIndex{0}) = s.value + +iterate(s::Some) = (s.value, nothing) +iterate( ::Some, s) = nothing + +ndims(::Some) = 0 +ndims(::Type{<:Some}) = 0 + +length(::Some) = 1 +size(::Some) = () +axes(::Some) = () + +IteratorSize(::Type{<:Some}) = HasShape{0}() +Broadcast.broadcastable(s::Some) = s + +eltype(::Type{Some{T}}) where {T} = T diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 19b049d9e5649..9e2786fc52f54 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -947,12 +947,9 @@ julia> string.(1:3, ". ", ["First", "Second", "Third"]) Sometimes, you want a container (like an array) that would normally participate in broadcast to be "protected" from broadcast's behavior of iterating over all of its elements. By placing it inside another container -(like a single element [`Tuple`](@ref)) broadcast will treat it as a single value. +(we recommend [`Some`](@ref)), broadcast will treat it as a single value. ```jldoctest -julia> ([1, 2, 3], [4, 5, 6]) .+ ([1, 2, 3],) -([2, 4, 6], [5, 7, 9]) - -julia> ([1, 2, 3], [4, 5, 6]) .+ tuple([1, 2, 3]) +julia> ([1, 2, 3], [4, 5, 6]) .+ Some([1, 2, 3]) ([2, 4, 6], [5, 7, 9]) ``` diff --git a/test/some.jl b/test/some.jl index e368c7b27c5dc..281737aaae1e9 100644 --- a/test/some.jl +++ b/test/some.jl @@ -98,3 +98,14 @@ using Base: notnothing # isnothing() @test !isnothing(1) @test isnothing(nothing) + +#Eltype +@test eltype(Some{Int}) == Int +@test eltype(Some(1)) == Int + +# Broadcast with Some +@testset "Some Broadcast" begin + @test Some(1) .+ 1 == 2 + @test Some([1, 2]) .+ [[1, 2,], [3, 4]] == [[2, 4], [4, 6]] + @test isa.(Some([1,2,3]), [Array, Dict, Int]) == [true, false, false] +end