diff --git a/Project.toml b/Project.toml index facebf5..b193577 100644 --- a/Project.toml +++ b/Project.toml @@ -1,18 +1,20 @@ name = "Static" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" authors = ["chriselrod", "ChrisRackauckas", "Tokazama"] -version = "1.2.0" +version = "1.3.0" [deps] CommonWorldInvalidations = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +SciMLPublic = "431bcebd-1456-4ced-9d72-93c2757fff0b" [compat] Aqua = "0.8.4" CommonWorldInvalidations = "1" IfElse = "0.1" PrecompileTools = "1.1" +SciMLPublic = "1.0.0" Test = "1" julia = "1.10" diff --git a/src/Static.jl b/src/Static.jl index 1f2ccef..207cf94 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -1,9 +1,15 @@ module Static import IfElse: ifelse +using SciMLPublic: @public export StaticInt, StaticFloat64, StaticSymbol, True, False, StaticBool, NDIndex -export dynamic, is_static, known, static, static_promote +export dynamic, is_static, known, static, static_promote, static_first, static_step, + static_last + +@public OptionallyStaticRange, +OptionallyStaticUnitRange, OptionallyStaticStepRange, SUnitRange, SOneTo +@public eachop, eachop_tuple, reduce_tup, eq, ne, gt, ge, le, lt, mul, add import PrecompileTools: @recompile_invalidations @@ -12,7 +18,7 @@ import PrecompileTools: @recompile_invalidations end """ - StaticSymbol + StaticSymbol(S::Symbol)::StaticSymbol{S} A statically typed `Symbol`. """ @@ -33,7 +39,7 @@ Base.Symbol(@nospecialize(s::StaticSymbol)) = known(s) abstract type StaticInteger{N} <: Number end """ - StaticBool(x::Bool) -> True/False + StaticBool(x::Bool)::Union{True, False} A statically typed `Bool`. """ @@ -55,7 +61,7 @@ function StaticBool(x::Bool) end """ - StaticInt(N::Int) -> StaticInt{N}() + StaticInt(N::Int)::StaticInt{N} A statically sized `Int`. Use `StaticInt(N)` instead of `Val(N)` when you want it to behave like a number. @@ -68,7 +74,7 @@ struct StaticInt{N} <: StaticInteger{N} end """ - IntType(x::Integer) -> Union{Int,StaticInt} + IntType(x::Integer)::Union{Int, StaticInt} `IntType` is a union of `Int` and `StaticInt`. As a function, it ensures that `x` one of the two. @@ -256,9 +262,10 @@ function static(x::X) where {X} end """ - is_static(::Type{T}) -> StaticBool + is_static(::Type{T})::Union{True, False} -Returns `True` if `T` is a static type. +If `T` is a static type return `static(true)::True` and otherwise returns +`static(false)::False` See also: [`static`](@ref), [`known`](@ref) """ @@ -582,7 +589,7 @@ permute(@nospecialize(x::Tuple), @nospecialize(perm::Val)) = permute(x, static(p end """ - eachop(op, args...; iterator::Tuple{Vararg{StaticInt}}) -> Tuple + Static.eachop(op, args...; iterator::Tuple{Vararg{StaticInt}})::Tuple Produces a tuple of `(op(args..., iterator[1]), op(args..., iterator[2]),...)`. """ @@ -592,7 +599,7 @@ end eachop(::F, ::Tuple{}, args::Vararg{Any}) where {F} = () """ - eachop_tuple(op, arg, args...; iterator::Tuple{Vararg{StaticInt}}) -> Type{Tuple} + Static.eachop_tuple(op, arg, args...; iterator::Tuple{Vararg{StaticInt}})::Type{Tuple} Produces a tuple type of `Tuple{op(arg, args..., iterator[1]), op(arg, args..., iterator[2]),...}`. Note that if one of the arguments passed to `op` is a `Tuple` type then it should be the first argument @@ -781,66 +788,109 @@ end end """ - eq(x, y) + Static.eq(x, y)::Union{Bool, True, False} -Equivalent to `!=` but if `x` and `y` are both static returns a `StaticBool. +Equivalent to `==` but if `x` and `y` are static the return value is a `StaticBool. """ eq(x::X, y::Y) where {X, Y} = ifelse(is_static(X) & is_static(Y), static, identity)(x == y) + +""" + Static.eq(x)::Base.Fix2{typeof(Static.eq}} + +Create a function that compares `x` to other values using `Static.eq` (i.e. a +function equivalent to `y -> Static.eq(y, x)`). +""" eq(x) = Base.Fix2(eq, x) """ - ne(x, y) + Static.ne(x, y)::Union{Bool, True, False} -Equivalent to `!=` but if `x` and `y` are both static returns a `StaticBool. +Equivalent to `!=` but if `x` and `y` are static the return value is a `StaticBool. """ ne(x::X, y::Y) where {X, Y} = !eq(x, y) + +""" + Static.ne(x)::Base.Fix2{typeof(Static.ne}} + +Create a function that compares `x` to other values using `Static.ne` (i.e. a +function equivalent to `y -> Static.ne(y, x))`. +""" ne(x) = Base.Fix2(ne, x) """ - gt(x, y) + Static.ne(x, y)::Union{Bool, True, False} -Equivalent to `>` but if `x` and `y` are both static returns a `StaticBool. +Equivalent to `>` but if `x` and `y` are static the return value is a `StaticBool. """ gt(x::X, y::Y) where {X, Y} = ifelse(is_static(X) & is_static(Y), static, identity)(x > y) + +""" + Static.gt(x)::Base.Fix2{typeof(Static.gt}} + +Create a function that compares `x` to other values using `Static.gt` (i.e. a +function equivalent to `y -> Static.gt(y, x))`. +""" gt(x) = Base.Fix2(gt, x) """ - ge(x, y) + Static.ge(x, y)::Union{Bool, True, False} -Equivalent to `>=` but if `x` and `y` are both static returns a `StaticBool. +Equivalent to `>=` but if `x` and `y` are static the return value is a `StaticBool. """ ge(x::X, y::Y) where {X, Y} = ifelse(is_static(X) & is_static(Y), static, identity)(x >= y) + +""" + Static.ge(x)::Base.Fix2{typeof(Static.ge}} + +Create a function that compares `x` to other values using `Static.ge` (i.e. a +function equivalent to `y -> Static.ge(y, x)`). +""" ge(x) = Base.Fix2(ge, x) """ - le(x, y) + Static.le(x, y)::Union{Bool, True, False} -Equivalent to `<=` but if `x` and `y` are both static returns a `StaticBool. +Equivalent to `<=` but if `x` and `y` are static the return value is a `StaticBool. """ le(x::X, y::Y) where {X, Y} = ifelse(is_static(X) & is_static(Y), static, identity)(x <= y) + +""" + Static.le(x)::Base.Fix2{typeof(Static.le}} + +Create a function that compares `x` to other values using `Static.le` (i.e. a +function equivalent to `y -> Static.le(y, x)`). +""" le(x) = Base.Fix2(le, x) """ - lt(x, y) + Static.lt(x, y)::Union{Bool, True, False} -Equivalent to `<` but if `x` and `y` are both static returns a `StaticBool. +Equivalent to `<` but if `x` and `y` are static the return value is a `StaticBool.` """ lt(x::X, y::Y) where {X, Y} = ifelse(is_static(X) & is_static(Y), static, identity)(x < y) + +""" + Static.lt(x)::Base.Fix2{typeof(Static.lt}} + +Create a function that compares `x` to other values using `Static.lt` (i.e. a +function equivalent to y -> Static.lt(y, x)). +""" lt(x) = Base.Fix2(lt, x) """ - mul(x) -> Base.Fix2(*, x) - mul(x, y) -> + Static.mul(x)::Base.Fix2{typeof(*)} -Equivalent to `*` but allows for lazy multiplication when passing functions. +Create a function that multiplies `x` with other values (i.e. a function +equivalent to `y -> y * x`). """ mul(x) = Base.Fix2(*, x) """ - add(x) -> Base.Fix2(+, x) - add(x, y) -> + Static.add(x) -> Base.Fix2(+, x) + Static.add(x, y) -Equivalent to `+` but allows for lazy addition when passing functions. +Create a function that adds `x` to other values (i.e. a function equivalent to +`y -> y + x`). """ add(x) = Base.Fix2(+, x) @@ -966,15 +1016,17 @@ end return (Base.to_index(A, I[1]), to_indices(A, indstail, Base.tail(I))...) end -function Base.show(io::IO, @nospecialize(x::Union{StaticNumber, StaticSymbol, NDIndex})) +function Base.show(@nospecialize(io::IO), @nospecialize(x::Union{ + StaticNumber, StaticSymbol, NDIndex})) show(io, MIME"text/plain"(), x) end -function Base.show(io::IO, ::MIME"text/plain", - @nospecialize(x::Union{StaticNumber, StaticSymbol})) +function Base.show( + @nospecialize(io::IO), ::MIME"text/plain", @nospecialize(x::Union{ + StaticNumber, StaticSymbol})) print(io, "static(" * repr(known(typeof(x))) * ")") nothing end -function Base.show(io::IO, m::MIME"text/plain", @nospecialize(x::NDIndex)) +function Base.show(@nospecialize(io::IO), m::MIME"text/plain", @nospecialize(x::NDIndex)) print(io, "NDIndex") show(io, m, Tuple(x)) nothing diff --git a/src/float.jl b/src/float.jl index 00a66d7..5d4b6d1 100644 --- a/src/float.jl +++ b/src/float.jl @@ -1,6 +1,6 @@ """ - StaticFloat64{N} + StaticFloat64(F::Float64)::StaticFloat64{F} A statically sized `Float64`. Use `StaticFloat64(N)` instead of `Val(N)` when you want it to behave like a number. diff --git a/src/ranges.jl b/src/ranges.jl index b93bb5a..3de504e 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -150,16 +150,78 @@ const OptionallyStaticRange{ F, L} = Union{OptionallyStaticUnitRange{F, L}, OptionallyStaticStepRange{F, <:Any, L}} -# these probide a generic method for extracting potentially static values. +""" + static_first(x::AbstractRange) + +Attempt to return `static(first(x))`, if known at compile time. Otherwise, return +`first(x)`. + +See also: [`static_step`](@ref), [`static_last`](@ref) + +# Examples + +```julia +julia> static_first(static(2):10) +static(2) + +julia> static_first(1:10) +1 + +julia> static_first(Base.OneTo(10)) +static(1) + +``` +""" static_first(x::Base.OneTo) = StaticInt(1) static_first(x::Union{Base.Slice, Base.IdentityUnitRange}) = static_first(x.indices) static_first(x::OptionallyStaticRange) = getfield(x, :start) static_first(x) = first(x) +""" + static_step(x::AbstractRange) + +Attempt to return `static(step(x))`, if known at compile time. Otherwise, return +`step(x)`. + +See also: [`static_first`](@ref), [`static_last`](@ref) + +# Examples + +```julia +julia> static_step(static(1):static(3):9) +static(3) + +julia> static_step(1:3:9) +3 + +julia> static_step(1:9) +static(1) + +``` +""" static_step(@nospecialize x::AbstractUnitRange) = StaticInt(1) static_step(x::OptionallyStaticStepRange) = getfield(x, :step) static_step(x) = step(x) +""" + static_last(x::AbstractRange) + +Attempt to return `static(last(x))`, if known at compile time. Otherwise, return +`last(x)`. + +See also: [`static_first`](@ref), [`static_step`](@ref) + +# Examples + +```julia +julia> static_last(static(1):static(10)) +static(10) + +julia> static_last(static(1):10) +10 + +``` +""" static_last(x::OptionallyStaticRange) = getfield(x, :stop) static_last(x) = last(x) static_last(x::Union{Base.Slice, Base.IdentityUnitRange}) = static_last(x.indices)