From 508be17074714bdc149a9c0ea1f173e51a17fb41 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Mon, 6 Jun 2022 11:33:33 +0000 Subject: [PATCH] add more functionlenses --- Project.toml | 4 +++- src/Accessors.jl | 1 + src/functionlenses.jl | 20 ++++++++++++++++++++ src/testing.jl | 6 +++--- test/test_functionlenses.jl | 18 ++++++++++++++++++ 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 94df4211..d3cc8475 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Accessors" uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" authors = ["Takafumi Arakaki ", "Jan Weidner and contributors"] -version = "0.1.12" +version = "0.1.13" [deps] Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" @@ -9,6 +9,7 @@ CompositionsBase = "a33af91c-f02d-484b-be07-31d278c5ca2b" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Future = "9fa8497b-333b-5362-9e8d-4d0656e87820" +InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Requires = "ae029012-a4dd-5104-9daa-d747884805df" @@ -18,6 +19,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Compat = "3.18, 4" CompositionsBase = "0.1" ConstructionBase = "1.2" +InverseFunctions = "0.1.5" MacroTools = "0.4.4, 0.5" Requires = "0.5, 1.0" StaticNumbers = "0.3" diff --git a/src/Accessors.jl b/src/Accessors.jl index 0a9dd895..db6eed41 100644 --- a/src/Accessors.jl +++ b/src/Accessors.jl @@ -2,6 +2,7 @@ module Accessors using MacroTools using MacroTools: isstructdef, splitstructdef, postwalk using Requires: @require +using InverseFunctions diff --git a/src/functionlenses.jl b/src/functionlenses.jl index a752f125..5d3feded 100644 --- a/src/functionlenses.jl +++ b/src/functionlenses.jl @@ -51,10 +51,30 @@ delete(path, ::typeof(dirname)) = basename(path) set(x::Real, ::typeof(real), y) = y set(x, ::typeof(real), y) = y + im*imag(x) set(x, ::typeof(imag), y) = real(x) + im*y +set(x, ::typeof(angle), y) = abs(x) * cis(y) +set(x, ::typeof(abs), y) = y >= 0 ? y * sign(x) : throw(DomainError(y, "cannot set abs($x) to $y")) set(arr, ::typeof(normalize), val) = norm(arr) * val set(arr, ::typeof(norm), val) = val/norm(arr) * arr # should we check val is positive? +# functions supported by inverse() +# https://github.com/JuliaMath/InverseFunctions.jl/blob/master/src/inverse.jl +set(x, f::Union{ + typeof.(( + identity, inv, adjoint, transpose, conj, + !, +, -, + exp, log, exp2, log2, exp10, log10, expm1, log1p, + sqrt, cbrt, deg2rad, rad2deg, + ))..., + Base.Fix1{typeof(+)}, Base.Fix2{typeof(+)}, + Base.Fix1{typeof(-)}, Base.Fix2{typeof(-)}, + Base.Fix1{typeof(*)}, Base.Fix2{typeof(*)}, + Base.Fix1{typeof(/)}, Base.Fix2{typeof(/)}, + Base.Fix1{typeof(\)}, Base.Fix2{typeof(\)}, + Base.Fix1{typeof(^)}, Base.Fix2{typeof(^)}, + Base.Fix1{typeof(log)}, Base.Fix2{typeof(log)}, +}, y) = inverse(f)(y) + ################################################################################ ##### dates ################################################################################ diff --git a/src/testing.jl b/src/testing.jl index c280728d..6281390e 100644 --- a/src/testing.jl +++ b/src/testing.jl @@ -1,13 +1,13 @@ using Test: @test -function test_getset_laws(lens, obj, val1, val2) +function test_getset_laws(lens, obj, val1, val2; cmp=(==)) # set ⨟ get val = lens(obj) - @test set(obj, lens, val) == obj + @test cmp(set(obj, lens, val), obj) # get ⨟ set obj1 = set(obj, lens, val1) - @test lens(obj1) == val1 + @test cmp(lens(obj1), val1) # set idempotent obj12 = set(obj1, lens, val2) diff --git a/test/test_functionlenses.jl b/test/test_functionlenses.jl index 6b8e6456..2a12d542 100644 --- a/test/test_functionlenses.jl +++ b/test/test_functionlenses.jl @@ -92,6 +92,24 @@ end @test 2.0 === @set real(1) = 2.0 @test 1.0 + 2im === @set imag(1) = 2.0 @test 1.0 + 2im === @set imag(1+1im) = 2.0 + + 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, _))] + x = 5 + test_getset_laws(o, x, 10, 20; cmp=isapprox) + @inferred set(x, o, 10) + end + + x = 3 + 4im + @test @set(abs(x) = 10) ≈ 6 + 8im + @test @set(angle(x) = π/2) ≈ 5im + @test_throws DomainError @set(abs(x) = -10) + + # composition + o = @optic 1/(1 + exp(-_)) + @test o(2) ≈ 0.8807970779778823 + @test @inferred(set(2, o, 0.999)) ≈ 6.906754778648465 end @testset "dates" begin