InitialValues.jl
InitialValuesInitialValues.InitialValueInitialValues.InitInitialValues.hasinitialvalueInitialValues.isknownInitialValues.@defInitialValues.@disambiguate
InitialValues — Module.InitialValues.jl: Canonical default initial values ("left identity") for Julia
InitialValues.jl provides a generic singleton initial value Init(f) that can be used as a₀ in f(a₀, x). For a binary operator op, it means that Init(op) acts like the left identity for any type of x:
julia> using InitialValues
julia> Init(+) + 1
1
julia> Init(+) + 1.0
1.0
julia> foldl(+, 1:3, init=Init(+))
6Following methods are defined for the binary operators in Base:
julia> Init(*) * 1
1
julia> Init(&) & 1
1
julia> Init(|) | 1
1
julia> min(Init(min), 1)
1
julia> max(Init(max), 1)
1
julia> Base.add_sum(Init(Base.add_sum), 1)
1
julia> Base.mul_prod(Init(Base.mul_prod), 1)
1Init is not called Identity because it is useful to define it for functions that are not binary operator (symmetric in signature). For example, push!! in BangBang.jl defines
julia> using BangBang
julia> push!!(Init(push!!), 1)
1-element Array{Int64,1}:
1This provides a powerful pattern when combined with foldl:
julia> foldl(push!!, (1, missing, 2.0), init=Init(push!!))
3-element Array{Union{Missing, Float64},1}:
1.0
missing
2.0Transducers.jl extensively uses Init.
As binary operators like * in Base are heavily overloaded, creating generic definitions such as above could have introduced method ambiguities. To protect against such situation, InitialValues.jl is tested using Aqua.jl.
InitialValues.Init — Function.Init(op) :: InitialValueCreate a generic (left) identity for a binary operator op. For general binary function, it provides an identity-like generic default value (see BangBang.push!!).
Examples
julia> using InitialValues
julia> Init(*) isa InitialValues.InitialValue
true
julia> Init(*) * 1
1
julia> Init(*) * missing
missing
julia> Init(*) * "right"
"right"
julia> Init(*) * :actual_anything_works
:actual_anything_works
julia> foldl(+, 1:3, init=Init(+))
6
julia> float(Init(*))
1.0
julia> Integer(Init(+))
0InitialValues.@def — Macro.InitialValues.@def op [y = :x]Define a generic (left) identity for a binary operator op. Specify the second argument for a binary function in general.
InitialValues.@def op is expanded to
op(::SpecificInitialValue{typeof(op)}, x) = x
InitialValues.hasinitialvalue(::Type{typeof(op)}) = trueFor operations like push!, it is useful to define the returned value to be different from x. This can be done by using the second argument to the maco; i.e., InitialValues.@def push! [x] is expanded to
push!(::SpecificInitialValue{typeof(push!)}, x) = [x]
InitialValues.hasinitialvalue(::Type{typeof(push!)}) = trueNote that the second argument to op is always x.
InitialValues.@disambiguate — Macro.InitialValues.@disambiguate op RightType [y = :x]Disambiguate the method introduced by @def.
It is expanded to
op(::SpecificInitialValue{typeof(op)}, x::RightType) = xInitialValues.hasinitialvalue — Function.InitialValues.hasinitialvalue(op) :: BoolExamples
julia> using InitialValues
julia> all(InitialValues.hasinitialvalue, [
*,
+,
&,
|,
min,
max,
Base.add_sum,
Base.mul_prod,
])
true
julia> InitialValues.hasinitialvalue((x, y) -> x + y)
falseInitialValues.isknown — Function.InitialValues.isknown(::InitialValue) :: BoolExamples
julia> using InitialValues
julia> InitialValues.isknown(Init(+))
true
julia> InitialValues.isknown(Init((x, y) -> x + y))
falseInitialValues.InitialValue — Type.InitialValues.InitialValueAn abstract super type of all generic initial value types.