From 46e45f9d7f1ad16c69474c7f70841fa8cfbc7715 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Wed, 6 May 2020 13:35:54 -0600 Subject: [PATCH 01/11] Define machinery so that Some can be used for broadcast --- base/some.jl | 22 ++++++++++++++++++++++ doc/src/manual/arrays.md | 7 ++----- test/some.jl | 11 +++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/base/some.jl b/base/some.jl index fbbc3aed0e284..cb8a235de9e01 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 it's enclosed value as a scalar. + Use [`something`](@ref) to access the value wrapped by a `Some` object. """ struct Some{T} @@ -95,3 +97,23 @@ something() = throw(ArgumentError("No value arguments present")) something(x::Nothing, y...) = something(y...) something(x::Some, y...) = x.value something(x::Any, y...) = x + +#Methods for broadcast +Base.getindex(s::Some) = s.value +Base.getindex(s::Some, ::CartesianIndex{0}) = s.value + +Base.iterate(s::Some) = (s.value, nothing) +Base.iterate( ::Some, s) = nothing + +Base.ndims(::Some) = 0 +Base.ndims(::Type{<:Some}) = 0 + +Base.length(::Some) = 1 +Base.size(::Some) = () +Base.axes(::Some) = () + +Base.IteratorSize(::Type{<:Some}) = Base.HasShape{0}() +Base.broadcastable(s::Some) = s + +Base.eltype(::Some{T}) where {T} = T +Base.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 From eebe50dd5e9efa8c33f40aac339b5409c18b9c29 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Wed, 6 May 2020 13:46:38 -0600 Subject: [PATCH 02/11] remove erroneous Base.____ --- base/some.jl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/base/some.jl b/base/some.jl index cb8a235de9e01..3b98691a0a240 100644 --- a/base/some.jl +++ b/base/some.jl @@ -99,21 +99,21 @@ something(x::Some, y...) = x.value something(x::Any, y...) = x #Methods for broadcast -Base.getindex(s::Some) = s.value -Base.getindex(s::Some, ::CartesianIndex{0}) = s.value +getindex(s::Some) = s.value +getindex(s::Some, ::CartesianIndex{0}) = s.value -Base.iterate(s::Some) = (s.value, nothing) -Base.iterate( ::Some, s) = nothing +iterate(s::Some) = (s.value, nothing) +iterate( ::Some, s) = nothing -Base.ndims(::Some) = 0 -Base.ndims(::Type{<:Some}) = 0 +ndims(::Some) = 0 +ndims(::Type{<:Some}) = 0 -Base.length(::Some) = 1 -Base.size(::Some) = () -Base.axes(::Some) = () +length(::Some) = 1 +size(::Some) = () +axes(::Some) = () -Base.IteratorSize(::Type{<:Some}) = Base.HasShape{0}() -Base.broadcastable(s::Some) = s +IteratorSize(::Type{<:Some}) = Base.HasShape{0}() +broadcastable(s::Some) = s -Base.eltype(::Some{T}) where {T} = T -Base.eltype(::Type{Some{T}}) where {T} = T +eltype(::Some{T}) where {T} = T +eltype(::Type{Some{T}}) where {T} = T From da00b1665cb82acabce9e7d137d551dc03b9ecc0 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Wed, 6 May 2020 13:58:42 -0600 Subject: [PATCH 03/11] another Base.___ --- base/some.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/some.jl b/base/some.jl index 3b98691a0a240..086d830706a09 100644 --- a/base/some.jl +++ b/base/some.jl @@ -112,7 +112,7 @@ length(::Some) = 1 size(::Some) = () axes(::Some) = () -IteratorSize(::Type{<:Some}) = Base.HasShape{0}() +IteratorSize(::Type{<:Some}) = HasShape{0}() broadcastable(s::Some) = s eltype(::Some{T}) where {T} = T From f3dbe391b72d65acd81e7605c3f9d5c3249a288f Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Wed, 6 May 2020 14:09:31 -0600 Subject: [PATCH 04/11] move Some broadcast definitions further down --- base/Base.jl | 2 ++ base/some.jl | 20 -------------------- base/somebroadcast.jl | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 base/somebroadcast.jl 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/some.jl b/base/some.jl index 086d830706a09..2e761466a8d61 100644 --- a/base/some.jl +++ b/base/some.jl @@ -97,23 +97,3 @@ something() = throw(ArgumentError("No value arguments present")) something(x::Nothing, y...) = something(y...) something(x::Some, y...) = x.value something(x::Any, y...) = x - -#Methods for 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}() -broadcastable(s::Some) = s - -eltype(::Some{T}) where {T} = T -eltype(::Type{Some{T}}) where {T} = T diff --git a/base/somebroadcast.jl b/base/somebroadcast.jl new file mode 100644 index 0000000000000..e6f588a12e83d --- /dev/null +++ b/base/somebroadcast.jl @@ -0,0 +1,19 @@ +#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}() +broadcastable(s::Some) = s + +eltype(::Some{T}) where {T} = T +eltype(::Type{Some{T}}) where {T} = T From 6a265ef1351aa69ba2d34f1e59c3bdef53d2234e Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 6 May 2020 14:54:21 -0600 Subject: [PATCH 05/11] Update base/somebroadcast.jl Co-authored-by: Matt Bauman --- base/somebroadcast.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/somebroadcast.jl b/base/somebroadcast.jl index e6f588a12e83d..c82a1c890b19e 100644 --- a/base/somebroadcast.jl +++ b/base/somebroadcast.jl @@ -13,7 +13,7 @@ size(::Some) = () axes(::Some) = () IteratorSize(::Type{<:Some}) = HasShape{0}() -broadcastable(s::Some) = s +Broadcast.broadcastable(s::Some) = s eltype(::Some{T}) where {T} = T eltype(::Type{Some{T}}) where {T} = T From cfeeb7fa4e1e3498a5233447c02d89c2a9e47b7d Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 6 May 2020 15:37:21 -0600 Subject: [PATCH 06/11] Remove superfluous eltype method --- base/somebroadcast.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/somebroadcast.jl b/base/somebroadcast.jl index c82a1c890b19e..ed8cc296696bf 100644 --- a/base/somebroadcast.jl +++ b/base/somebroadcast.jl @@ -15,5 +15,4 @@ axes(::Some) = () IteratorSize(::Type{<:Some}) = HasShape{0}() Broadcast.broadcastable(s::Some) = s -eltype(::Some{T}) where {T} = T eltype(::Type{Some{T}}) where {T} = T From 43262e439d425d5d2faa87de56973bd8724f2015 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 6 May 2020 15:43:17 -0600 Subject: [PATCH 07/11] Update NEWS.md --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) 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 ---------------- From efba159605ab29e63b492c29562696616e2690aa Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Wed, 6 May 2020 17:53:54 -0600 Subject: [PATCH 08/11] make broadcastable default to Some instead of Ref --- base/broadcast.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 168431b5566d9..8aa269efb0b57 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -675,8 +675,8 @@ julia> Broadcast.broadcastable("hello") # Strings break convention of matching i Base.RefValue{String}("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) From 9123ab96e1bcf6bb93fae69f4d4db441ee0e8516 Mon Sep 17 00:00:00 2001 From: Mason Protter Date: Wed, 6 May 2020 18:57:24 -0600 Subject: [PATCH 09/11] update broadcastable docstring --- base/broadcast.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 8aa269efb0b57..030c7f4d73385 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -669,10 +669,10 @@ julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already s 3 julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars -Base.RefValue{Type{Int64}}(Int64) +Some(Int) 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}) = Some(x) From 827b017e3f72e82cf5bd0a77b84fec3bc368e7ee Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Thu, 7 May 2020 14:02:14 -0600 Subject: [PATCH 10/11] fix broken doctest --- base/broadcast.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 030c7f4d73385..3b7daae51abf2 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -668,8 +668,8 @@ 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 -Some(Int) +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 Some("hello") From fdcdb35461291b73a6b0d2a41d8f5c98a8155478 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Thu, 7 May 2020 16:26:09 -0600 Subject: [PATCH 11/11] it's -> its --- base/some.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/some.jl b/base/some.jl index 2e761466a8d61..e94bf0ebfcb1a 100644 --- a/base/some.jl +++ b/base/some.jl @@ -6,7 +6,7 @@ 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 it's enclosed value as a scalar. +`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. """