diff --git a/examples/specter.jl b/examples/specter.jl index 3acd23e5..c559894d 100644 --- a/examples/specter.jl +++ b/examples/specter.jl @@ -100,7 +100,7 @@ out = getall(data, optic) data = [2, 1, 3, 6, 9, 4, 8] out = @set data |> Filter(isodd) |> _[end] += 1 @test out == [2, 1, 3, 6, 10, 4, 8] -@test_broken eltype(out) == Int +@test eltype(out) == Int # ### Remove nils from a nested sequence diff --git a/src/functionlenses.jl b/src/functionlenses.jl index f2d8dc63..1c5253f0 100644 --- a/src/functionlenses.jl +++ b/src/functionlenses.jl @@ -53,6 +53,8 @@ set(x, ::typeof(imag), y) = real(x) + im*y set(x, ::typeof(angle), y) = abs(x) * cis(y) set(x, ::typeof(abs), y) = y >= zero(y) ? y * sign(x) : throw(DomainError(y, "cannot set abs($x) to $y")) +set(x, ::typeof(mod2pi), y) = 0 <= y <= 2π ? 2π * fld(x, 2π) + y : throw(DomainError(y, "cannot set mod2pi")) + set(arr, ::typeof(normalize), val) = norm(arr) * val set(arr, ::typeof(norm), val) = val/norm(arr) * arr # should we check val is positive? diff --git a/src/setindex.jl b/src/setindex.jl index e97c7625..7a7ef176 100644 --- a/src/setindex.jl +++ b/src/setindex.jl @@ -4,8 +4,9 @@ end @inline setindex(::Base.RefValue, val) = Ref(val) -Base.@propagate_inbounds function setindex(xs::AbstractArray, v, I...) - T = promote_type(eltype(xs), typeof(v)) +Base.@propagate_inbounds function setindex(xs::AbstractArray, v, I_raw...) + I = to_indices(xs, I_raw) + T = promote_type(eltype(xs), I isa Tuple{Vararg{Integer}} ? typeof(v) : eltype(v)) ys = similar(xs, T) if eltype(xs) !== Union{} copy!(ys, xs) @@ -31,3 +32,9 @@ end @inline setindex(nt::NamedTuple, v, idx::Tuple{Vararg{Symbol}}) = merge(nt, NamedTuple{idx}(v)) @inline setindex(p::Pair, v, idx::Integer) = Pair(setindex(Tuple(p), v, idx)...) + +@inline function setindex(x::Number, v, idx::Integer) + @boundscheck idx == only(eachindex(x)) || throw(BoundsError(x, idx)) + return v +end +@inline setindex(x::Number, v) = v diff --git a/test/test_core.jl b/test/test_core.jl index 27d8fcc4..71c0b9b5 100644 --- a/test/test_core.jl +++ b/test/test_core.jl @@ -242,10 +242,19 @@ end @test l(obj) == 1 @test set(obj, l, 6) == (6,2,3) + obj = 123 + for l in (@optic(_[1]), @optic(_[end]), @optic(_[])) + @test l(obj) === 123 + @test set(obj, l, 456) === 456 + end - l = @optic _[1:3] - @test l isa Accessors.IndexLens - @test l([4,5,6,7]) == [4,5,6] + obj = [:a, :b, :c] + l = @optic _[1:2] + @test @inferred(l(obj))::Vector{Symbol} == [:a, :b] + @test @inferred(set(obj, l, [:x, :y]))::Vector{Symbol} == [:x, :y, :c] + l = @optic _[CartesianIndex(2)] + @test @inferred(l(obj)) == :b + @test @inferred(set(obj, l, :x))::Vector{Symbol} == [:a, :x, :c] nt = (a=1, b=2, c=3) l = @optic _[(:a, :c)] diff --git a/test/test_functionlenses.jl b/test/test_functionlenses.jl index 2d23ec4d..00c36a57 100644 --- a/test/test_functionlenses.jl +++ b/test/test_functionlenses.jl @@ -96,9 +96,14 @@ end @test 1u"m" === @set real(2u"m") = 1u"m" @test (2 + 1im)u"m" === @set imag(2u"m") = 1u"m" + test_getset_laws(mod2pi, 5.3, 1, 2; cmp=isapprox) + test_getset_laws(mod2pi, -5.3, 1, 2; cmp=isapprox) + test_getset_laws(!, true, true, false) - # no need for extensive testing: all invertible lenses are simply forwarded to InverseFunctions - @testset for o in [inv, +, exp, sqrt, @optic(2 + _), @optic(_ * 3), @optic(log(2, _))] + @testset for o in [ + # invertible lenses below: no need for extensive testing, simply forwarded to InverseFunctions + inv, +, exp, sqrt, @optic(2 + _), @optic(_ * 3), @optic(log(2, _)) + ] x = 5 test_getset_laws(o, x, 10, 20; cmp=isapprox) @inferred set(x, o, 10) diff --git a/test/test_setindex.jl b/test/test_setindex.jl index be57a727..f33ab5b0 100644 --- a/test/test_setindex.jl +++ b/test/test_setindex.jl @@ -31,6 +31,9 @@ end @test arr == [1,2,3] @test Accessors.setindex(arr, 10.0, 1) ==ₜ Float64[10.0, 2.0, 3.0] + @test Accessors.setindex([1. 2; 3 4], zeros(2), 1, :) ==ₜ [0. 0; 3 4] + @test Accessors.setindex([[i, j] for i in 1:2, j in 1:2], [im, im], 1, :) ==ₜ Any[[im] [im]; [[2, 1]] [[2, 2]]] + d = Dict(:a => 1, :b => 2) @test_throws MethodError Base.setindex(d, 10, :a) @test Accessors.setindex(d, 10, :a) == Dict(:a=>10, :b=>2) @@ -53,7 +56,9 @@ end @set ref[] = "no mutation" @test ref[] === 1 @test typeof(ref) == Base.RefValue{Int} +end +@testset begin if VERSION >= v"1.5.0" _ = ref_alloc_test() @test @allocated(ref_alloc_test()) == 0