Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add INIT that works with arbitrary operators #29

Merged
merged 2 commits into from May 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/index.md
Expand Up @@ -6,6 +6,7 @@
```@docs
InitialValues
InitialValues.Init
InitialValues.INIT
InitialValues.@def
InitialValues.@def_monoid
InitialValues.@disambiguate
Expand Down
40 changes: 37 additions & 3 deletions src/InitialValues.jl
Expand Up @@ -6,7 +6,7 @@ module InitialValues
replace(read(path, String), r"^```julia"m => "```jldoctest README")
end InitialValues

export Init, asmonoid
export INIT, Init, asmonoid

"""
Init(op) :: InitialValue
Expand Down Expand Up @@ -55,10 +55,44 @@ An abstract super type of all generic initial value types.
"""
abstract type InitialValue end
abstract type SpecificInitialValue{OP} <: InitialValue end
# abstract type GenericIdentity <: AbstractIdentity end
abstract type NonspecificInitialValue <: InitialValue end

struct TypeOfINIT <: NonspecificInitialValue end

"""
INIT :: InitialValue

A generic initial value. Unlike [`Init`](@ref), this does not detect
an error when `INIT` is used with unintended operations.

# Examples
```jldoctest
julia> using InitialValues

julia> Init(+) * 0 # `Init(op)` must be used with `op`
ERROR: MethodError: no method matching *(::InitialValues.InitialValueOf{typeof(+)}, ::Int64)
[...]

julia> INIT * 123
123

julia> foldl(+, 1:3, init=INIT)
6
"""
const INIT = TypeOfINIT()

function Base.show(io::IO, ::TypeOfINIT) where {OP}
if !get(io, :limit, false)
# Don't show full name in REPL etc.:
print(io, "InitialValues.")
end
print(io, "INIT")
end

struct InitialValueOf{OP} <: SpecificInitialValue{OP} end

const GenericInitialValue{OP} = Union{SpecificInitialValue{OP},NonspecificInitialValue}

function Base.show(io::IO, ::InitialValueOf{OP}) where {OP}
if !get(io, :limit, false)
# Don't show full name in REPL etc.:
Expand All @@ -72,7 +106,7 @@ function Base.show(io::IO, ::InitialValueOf{OP}) where {OP}
end
end

itypeof_impl(op) = :(SpecificInitialValue{typeof($op)})
itypeof_impl(op) = :(GenericInitialValue{typeof($op)})
@eval itypeof(op) = $(itypeof_impl(:op))

"""
Expand Down
8 changes: 8 additions & 0 deletions test/test_basics.jl
Expand Up @@ -8,6 +8,9 @@ OPS = [*, +, |, &, min, max, Base.add_sum, Base.mul_prod]

@testset for op in OPS
@test op(Init(op), :anything) === :anything
@test op(:anything, Init(op)) === :anything
@test op(INIT, :anything) === :anything
@test op(:anything, INIT) === :anything
@test hasinitialvalue(op)
@test hasinitialvalue(typeof(op))
@test isknown(Init(op))
Expand All @@ -26,6 +29,11 @@ end
@test repr(Init(op)) == "InitialValues.$desired"
@test string(Init(op)) == "InitialValues.$desired"
end
@testset "INIT" begin
@test repr(INIT; context=:limit => true) == "INIT"
@test repr(INIT) == "InitialValues.INIT"
@test string(INIT) == "InitialValues.INIT"
end
end

@testset "hasinitialvalue" begin
Expand Down