InitialValues.jl
InitialValues
InitialValues.InitialValue
InitialValues.Init
InitialValues.hasinitialvalue
InitialValues.isknown
InitialValues.@def
InitialValues.@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(+))
6
Following 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)
1
Init
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}:
1
This 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.0
Transducers.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) :: InitialValue
Create 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(+))
0
InitialValues.@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)}) = true
For 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!)}) = true
Note 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) = x
InitialValues.hasinitialvalue
— Function.InitialValues.hasinitialvalue(op) :: Bool
Examples
julia> using InitialValues
julia> all(InitialValues.hasinitialvalue, [
*,
+,
&,
|,
min,
max,
Base.add_sum,
Base.mul_prod,
])
true
julia> InitialValues.hasinitialvalue((x, y) -> x + y)
false
InitialValues.isknown
— Function.InitialValues.isknown(::InitialValue) :: Bool
Examples
julia> using InitialValues
julia> InitialValues.isknown(Init(+))
true
julia> InitialValues.isknown(Init((x, y) -> x + y))
false
InitialValues.InitialValue
— Type.InitialValues.InitialValue
An abstract super type of all generic initial value types.