BangBang.jl
BangBang.BangBangBangBang.ExtrasBangBang.NoBang.EmptyBangBang.AccessorsImpl.prefermutationBangBang.Extras.modify!!BangBang.NoBang.singletonofBangBang.add!!BangBang.append!!BangBang.broadcast!!BangBang.collectorBangBang.delete!!BangBang.deleteat!!BangBang.empty!!BangBang.finish!BangBang.intersect!!BangBang.lmul!!BangBang.materialize!!BangBang.merge!!BangBang.mergewith!!BangBang.mul!!BangBang.pop!!BangBang.popfirst!!BangBang.push!!BangBang.pushfirst!!BangBang.rmul!!BangBang.setdiff!!BangBang.setindex!!BangBang.setproperties!!BangBang.setproperty!!BangBang.splice!!BangBang.symdiff!!BangBang.union!!BangBang.unique!!BangBang.@!BangBang.AccessorsImpl.@set!!
BangBang.BangBang — ModuleBangBang
BangBang.jl implements functions whose name ends with !!. Those functions provide a uniform interface for mutable and immutable data structures. Furthermore, those functions implement the "widening" fallback for the case when the usual mutating function does not work (e.g., push!!(Int[], 1.5) creates a new array Float64[1.5]).
See the supported functions in the documentation
BangBang.add!! — Functionadd!!(A, B) -> A′A .+= B if possible; otherwise return A .+ B.
Examples
julia> using BangBang: add!!
julia> add!!((1,), (2,))
(3,)
julia> add!!([1], [2])
1-element Vector{Int64}:
3BangBang.append!! — Functionappend!!(dest, src) -> dest′Append items in src to dest. Mutate dest if possible.
This function "owns" dest but not src; i.e., returned value dest′ does not alias src. For example, append!!(Empty(Vector), src) shallow-copies src instead of returning src as-is.
See also push!!.
Examples
julia> using BangBang
julia> append!!((1, 2), (3, 4))
(1, 2, 3, 4)
julia> append!!([1, 2], (3, 4))
4-element Vector{Int64}:
1
2
3
4
julia> using StaticArrays: SVector
julia> @assert append!!(SVector(1, 2), (3, 4)) === SVector(1, 2, 3, 4)
julia> using DataFrames: DataFrame
julia> @assert append!!(DataFrame(a=[1], b=[2]), [(a=3.0, b=4.0)]) ==
DataFrame(a=[1.0, 3.0], b=[2.0, 4.0])
julia> using StructArrays: StructVector
julia> @assert append!!(StructVector(a=[1], b=[2]), [(a=3.5, b=4.5)]) ==
StructVector(a=[1.0, 3.5], b=[2.0, 4.5])
julia> using TypedTables: Table
julia> @assert append!!(Table(a=[1], b=[2]), [(a=3.5, b=4.5)]) ==
Table(a=[1.0, 3.5], b=[2.0, 4.5])append!! does not own the second argument:
julia> xs = [1, 2, 3];
julia> ys = append!!(Empty(Vector), xs)
3-element Vector{Int64}:
1
2
3
julia> ys === xs
falseBangBang.broadcast!! — Functionbroadcast!!(f, dest, As...) -> dest′A mutate-or-widen version of dest .= f.(As...).
BangBang.collector — Functioncollector(data::AbstractVector, unsafe::Val = Val(false)) -> c::AbstractCollector
collector(ElType::Type = Union{}) -> c::AbstractCollectorCreate a "collector" c that can be used to collect elements; i.e., it supports append!! and push!!. Appending and pushing elements to a collector are more efficient than doing these operations directly to a vector.
Use finish!(c) to get the collected data as a vector.
push!! on the collector can be further optimized by passing Val(true) to the second unsafe argument. This is valid to use only if the number of elements appended to c is less than or equal to length(data).
Examples
julia> using BangBang
julia> c = collector()
c = push!!(c, 1)
c = push!!(c, 0.5)
finish!(c)
2-element Vector{Float64}:
1.0
0.5
julia> finish!(append!!(collector(), (x for x in Any[1, 2.0, 3im])))
3-element Vector{ComplexF64}:
1.0 + 0.0im
2.0 + 0.0im
0.0 + 3.0im
julia> finish!(append!!(collector(Vector{Float64}(undef, 10), Val(true)), [1, 2, 3]))
3-element Vector{Float64}:
1.0
2.0
3.0BangBang.delete!! — Functiondelete!!(assoc, key) -> assoc′Examples
julia> using BangBang
julia> delete!!((a=1, b=2), :a)
(b = 2,)
julia> delete!!(Dict(:a=>1, :b=>2), :a)
Dict{Symbol, Int64} with 1 entry:
:b => 2BangBang.deleteat!! — Functiondeleteat!!(assoc, i) -> assoc′Examples
julia> using BangBang
julia> deleteat!!((1, 2, 3), 2)
(1, 3)
julia> deleteat!!([1, 2, 3], 2)
2-element Vector{Int64}:
1
3
julia> using StaticArrays: SVector
julia> @assert deleteat!!(SVector(1, 2, 3), 2) === SVector(1, 3)BangBang.empty!! — Functionempty!!(collection) -> collection′Examples
julia> using BangBang
julia> empty!!((1, 2, 3))
()
julia> empty!!((a=1, b=2, c=3))
NamedTuple()
julia> xs = [1, 2, 3];
julia> empty!!(xs)
Int64[]
julia> xs
Int64[]
julia> using StaticArrays: SVector
julia> @assert empty!!(SVector(1, 2)) == SVector{0, Int}()BangBang.finish! — Functionfinish!(c::AbstractCollector) -> data::AbstractVectorExtract the data collected in the collector c.
See collector.
BangBang.intersect!! — Functionintersect!!(setlike, others...) -> setlike′Return the set of elements in setlike and in all the collections others. Mutate setlike if possible.
This function "owns" setlike but not others; i.e., returned value setlike′ does not alias any of others. For example, other = Set([1]); intersect!!(Set([1, 2]), other) does not return other as-is.
Examples
julia> using BangBang
julia> xs = Set([1, 2]);
julia> ys = intersect!!(xs, [1]); # mutates `xs` as it's possible
julia> ys == Set([1])
true
julia> ys === xs # `xs` is returned
trueBangBang.lmul!! — Functionlmul!!(A, B) -> B′BangBang.materialize!! — Methodmaterialize!!(dest, x)julia> using BangBang
julia> using Base.Broadcast: instantiate, broadcasted
julia> bc = instantiate(broadcasted(+, [1.0, 1.5, 2.0], 1));
julia> xs = zeros(Float64, 3);
julia> ys = materialize!!(xs, bc)
3-element Vector{Float64}:
2.0
2.5
3.0
julia> xs === ys # mutated
true
julia> xs = Vector{Union{}}(undef, 3);
julia> ys = materialize!!(xs, bc)
3-element Vector{Float64}:
2.0
2.5
3.0
julia> xs === ys
falseBangBang.merge!! — Functionmerge!!(dictlike, others...) -> dictlike′
merge!!(combine, dictlike, others...) -> dictlike′Merge key-value pairs from others to dictlike. Mutate dictlike if possible.
This function "owns" dictlike but not others; i.e., returned value dictlike′ does not alias any of others. For example, merge!!(Empty(Dict), other) shallow-copies other instead of returning other as-is.
Method merge!!(combine::Union{Function,Type}, args...) as an alias of mergewith!!(combine, args...) is still available for backward compatibility.
See also mergewith!!.
Examples
julia> using BangBang
julia> merge!!(Dict(:a => 1), Dict(:b => 0.5))
Dict{Symbol, Float64} with 2 entries:
:a => 1.0
:b => 0.5
julia> merge!!((a = 1,), Dict(:b => 0.5))
(a = 1, b = 0.5)
julia> merge!!(+, Dict(:a => 1), Dict(:a => 0.5))
Dict{Symbol, Float64} with 1 entry:
:a => 1.5merge!! does not own the second argument:
julia> xs = Dict(:a => 1, :b => 2, :c => 3);
julia> ys = merge!!(Empty(Dict), xs)
Dict{Symbol, Int64} with 3 entries:
:a => 1
:b => 2
:c => 3
julia> ys === xs
falseBangBang.mergewith!! — Functionmergewith!!(combine, dictlike, others...) -> dictlike′
mergewith!!(combine)Like merge!!(combine, dictlike, others...) but combine does not have to be a Function.
This function "owns" dictlike but not others. See merge!! for more details.
The curried form mergewith!!(combine) returns the function (args...) -> mergewith!!(combine, args...).
BangBang.mul!! — Functionmul!!(C, A, B, [α, β]) -> C′BangBang.pop!! — Functionpop!!(sequence) -> (sequence′, value)
pop!!(assoc, key) -> (assoc′, value)
pop!!(assoc, key, default) -> (assoc′, value)Examples
julia> using BangBang
julia> pop!!([0, 1])
([0], 1)
julia> pop!!((0, 1))
((0,), 1)
julia> pop!!(Dict(:a => 1), :a)
(Dict{Symbol, Int64}(), 1)
julia> pop!!((a=1,), :a)
(NamedTuple(), 1)
julia> using StaticArrays: SVector
julia> @assert pop!!(SVector(1, 2)) === (SVector(1), 2)BangBang.popfirst!! — Functionpopfirst!!(sequence) -> (sequence′, value)Examples
julia> using BangBang
julia> popfirst!!([0, 1])
([1], 0)
julia> popfirst!!((0, 1))
((1,), 0)
julia> popfirst!!((a=0, b=1))
((b = 1,), 0)
julia> using StaticArrays: SVector
julia> @assert popfirst!!(SVector(1, 2)) === (SVector(2), 1)BangBang.push!! — Functionpush!!(collection, items...)Push one or more items to collection. Create a copy of collection if it cannot be mutated or the element type does not match.
Examples
julia> using BangBang
julia> push!!((1, 2), 3)
(1, 2, 3)
julia> push!!([1, 2], 3)
3-element Vector{Int64}:
1
2
3
julia> push!!([1, 2], 3.0)
3-element Vector{Float64}:
1.0
2.0
3.0
julia> using StaticArrays: SVector
julia> @assert push!!(SVector(1, 2), 3.0) === SVector(1.0, 2.0, 3.0)
julia> using DataFrames: DataFrame
julia> @assert push!!(DataFrame(a=[1], b=[2]), (a=3.5, b=4.5)) ==
DataFrame(a=[1.0, 3.5], b=[2.0, 4.5])
julia> using StructArrays: StructVector
julia> @assert push!!(StructVector(a=[1], b=[2]), (a=3.5, b=4.5)) ==
StructVector(a=[1.0, 3.5], b=[2.0, 4.5])
julia> using TypedTables: Table
julia> @assert push!!(Table(a=[1], b=[2]), (a=3.5, b=4.5)) ==
Table(a=[1.0, 3.5], b=[2.0, 4.5])BangBang.pushfirst!! — Functionpushfirst!!(collection, items...)Examples
julia> using BangBang
julia> pushfirst!!((1, 2), 3, 4)
(3, 4, 1, 2)
julia> pushfirst!!([1, 2], 3, 4)
4-element Vector{Int64}:
3
4
1
2
julia> pushfirst!!([1, 2], 3, 4.0)
4-element Vector{Float64}:
3.0
4.0
1.0
2.0
julia> using StaticArrays: SVector
julia> @assert pushfirst!!(SVector(1, 2), 3, 4) === SVector(3, 4, 1, 2)BangBang.rmul!! — Functionrmul!!(A, B) -> A′BangBang.setdiff!! — Functionsetdiff!!(setlike, others...) -> setlike′Return the set of elements in setlike but not in any of the collections others. Mutate setlike if possible.
This function "owns" setlike but not others; i.e., returned value setlike′ does not alias any of others. For example, other = Set([]); setdiff!!(Empty(Set), other) does not return other as-is.
Examples
julia> using BangBang
julia> xs = Set([1]);
julia> ys = setdiff!!(xs, [1]); # mutates `xs` as it's possible
julia> ys == Set([])
true
julia> ys === xs # `xs` is returned
trueBangBang.setindex!! — Functionsetindex!!(collection, value, indices...) -> collection′Examples
julia> using BangBang
julia> setindex!!((1, 2), 10.0, 1)
(10.0, 2)
julia> setindex!!([1, 2], 10.0, 1)
2-element Vector{Float64}:
10.0
2.0
julia> using StaticArrays: SVector
julia> @assert setindex!!(SVector(1, 2), 10.0, 1) == SVector(10.0, 2.0)BangBang.setproperties!! — Methodsetproperties!!(value, patch::NamedTuple)
setproperties!!(value; patch...)Examples
julia> using BangBang
julia> setproperties!!((a=1, b=2); b=3)
(a = 1, b = 3)
julia> struct Immutable
a
b
end
julia> setproperties!!(Immutable(1, 2); b=3)
Immutable(1, 3)
julia> mutable struct Mutable{T, S}
a::T
b::S
end
julia> s = Mutable(1, 2);
julia> setproperties!!(s; b=3)
Mutable{Int64, Int64}(1, 3)
julia> setproperties!!(s, b=4.0)
Mutable{Int64, Float64}(1, 4.0)
julia> s
Mutable{Int64, Int64}(1, 3)BangBang.setproperty!! — Functionsetproperty!!(value, name::Symbol, x)An alias of setproperties!!(value, (name=x,)).
BangBang.splice!! — Functionsplice!!(sequence, i, [replacement]) -> (sequence′, item)Examples
julia> using BangBang
julia> splice!!([1, 2, 3], 2)
([1, 3], 2)
julia> splice!!((1, 2, 3), 2)
((1, 3), 2)
julia> using StaticArrays: SVector
julia> @assert splice!!(SVector(1, 2, 3), 2) === (SVector(1, 3), 2)BangBang.symdiff!! — Functionsymdiff!!(setlike, others...) -> setlike′Return the set of elements that occur an odd number of times in setlike and the others collections. Mutate setlike if possible.
This function "owns" setlike but not others; i.e., returned value setlike′ does not alias any of others. For example, symdiff!!(Empty(Set), other) shallow-copies other instead of returning other as-is.
Examples
julia> using BangBang
julia> xs = Set([1, 2]);
julia> ys = symdiff!!(xs, [2, 3]); # mutates `xs` as it's possible
julia> ys == Set([1, 3])
true
julia> ys === xs # `xs` is returned
trueBangBang.union!! — Functionunion!!(setlike, others...) -> setlike′Return the union of all sets in the arguments. Mutate setlike if possible.
This function "owns" setlike but not others; i.e., returned value setlike′ does not alias any of others. For example, union!!(Empty(Set), other) shallow-copies other instead of returning other as-is.
Examples
julia> using BangBang
julia> xs = Set([1]);
julia> ys = union!!(xs, Set([2])); # mutates `xs` as it's possible
julia> ys == Set([1, 2])
true
julia> ys === xs # `xs` is returned
true
julia> zs = union!!(xs, Set([0.5])); # incompatible element type
julia> zs == Set([0.5, 1, 2])
true
julia> zs === xs # a new set is returned
falseunion!! does not own the second argument:
julia> xs = Set([1]);
julia> ys = union!!(Empty(Set), xs)
Set{Int64} with 1 element:
1
julia> ys === xs
falseBangBang.unique!! — Functionunique!!(set) -> set
unique!!(sequence) -> sequence′BangBang.@! — Macro@! exprConvert all supported mutating calls to double bang equivalent.
Examples
julia> using BangBang
julia> @! push!(empty!((0, 1)), 2, 3)
(2, 3)
julia> y = [1, 2];
julia> @! y .= 2 .* y
y
2-element Vector{Int64}:
2
4
julia> y = (1, 2);
julia> @! y .= 2 .* y
y
(2, 4)BangBang.NoBang.Empty — TypeEmpty(T)Create a proxy of an empty container of type T.
This is a simple API for solving problems such as:
- There is no consistent way to create an empty container given its type.
- There is no consistent way to know that nothing was appended into the container in type-domain.
Internally, this function simply works by creating a singleton container (a container with one element) using singletonof when the first element is push!!'ed.
Examples
julia> using BangBang
julia> push!!(Empty(Vector), 1)
1-element Vector{Int64}:
1
julia> append!!(Empty(Dict), (:a=>1, :b=>2))
Dict{Symbol, Int64} with 2 entries:
:a => 1
:b => 2
julia> using DataFrames: DataFrame
julia> @assert push!!(Empty(DataFrame), (a=1, b=2)) == DataFrame(a=[1], b=[2])
julia> using StructArrays: StructVector
julia> @assert push!!(Empty(StructVector), (a=1, b=2)) == StructVector(a=[1], b=[2])
julia> using TypedTables: Table
julia> @assert push!!(Empty(Table), (a=1, b=2)) == Table(a=[1], b=[2])
julia> using StaticArrays: SVector
julia> @assert push!!(Empty(SVector), 1) === SVector(1)Empty(T) object is an iterable with length 0 and element type Union{}:
julia> collect(Empty(Vector))
Union{}[]
julia> length(Empty(Vector))
0
julia> eltype(typeof(Empty(Vector)))
Union{}
julia> Base.IteratorSize(Empty)
Base.HasLength()
julia> Base.IteratorEltype(Empty)
Base.HasEltype()BangBang.NoBang.singletonof — Methodsingletonof(::Type{T}, x) :: T
singletonof(::T, x) :: TCreate a singleton container of type T.
Examples
julia> using BangBang
julia> @assert singletonof(Vector, 1) == [1]
julia> @assert singletonof(Dict, :a => 1) == Dict(:a => 1)
julia> @assert singletonof(Set, 1) == Set([1])
julia> using StructArrays: StructVector
julia> @assert singletonof(StructVector, (a=1, b=2)) == StructVector(a=[1], b=[2])
julia> using TypedTables: Table
julia> @assert singletonof(Table, (a=1, b=2)) == Table(a=[1], b=[2])
julia> using StaticArrays: SArray, SVector
julia> @assert singletonof(SArray, 1) === SVector(1)
julia> @assert singletonof(SVector, 1) === SVector(1)BangBang.AccessorsImpl.prefermutation — Functionprefermutation(lens::Lens) :: LensSee also @set!!.
BangBang.AccessorsImpl.@set!! — Macro@set!! assignmentLike Accessors.@set, but prefer mutation if possible.
Examples
julia> using BangBang
julia> mutable struct Mutable
a
b
end
julia> x = orig = Mutable((x=Mutable(1, 2), y=3), 4);
julia> @set!! x.a.x.a = 10;
julia> @assert x.a.x.a == orig.a.x.a == 10BangBang.Extras — ModuleBangBang.ExtrasBangBang APIs that have no counterparts in Base.
BangBang.Extras.modify!! — Functionmodify!!(f, dictlike, key) -> (dictlike′, ret)Lookup and then update, insert or delete in one go. If supported (e.g., when dictlike isa Dict), it is done without re-computing the hash. Immutable containers like NamedTuple is also supported.
The callable f must accept a single argument of type Union{Some{valtype(dictlike)}, Nothing}. The value Some(dictlike[key]) is passed to f if haskey(dictlike, key); otherwise nothing is passed.
If f returns nothing, corresponding entry in the dictionary dictlike is removed. If f returns non-nothing value x, key => something(x) is inserted to dictlike (equivalent to dictlike[key] = something(x) but more efficient).
modify!! returns a 2-tuple (dictlike′, ret) where dictlike′ is an updated version of dictlike (which may be identical to dictlike) and ret is the returned value of f.
This API is inspired by Control.Lens.At of Haskell's lens library.
Examples
julia> using BangBang.Extras: modify!!
julia> dict = Dict("a" => 1);
julia> dict′, ret = modify!!(dict, "a") do val
Some(something(val, 0) + 1)
end;
julia> ret
Some(2)
julia> dict === dict′
true
julia> dict
Dict{String, Int64} with 1 entry:
"a" => 2
julia> dict = Dict();
julia> modify!!(dict, "a") do val
Some(something(val, 0) + 1)
end;
julia> dict
Dict{Any, Any} with 1 entry:
"a" => 1
julia> modify!!(_ -> nothing, dict, "a");
julia> dict
Dict{Any, Any}()Discussion