From 27bdfb84791fa73ee7439c6bc715205e33c50060 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 13 Oct 2019 19:04:03 -0700 Subject: [PATCH] Add at-set!! macro Note: I have to exclude get/set from ambiguity checks again. So, a part of commit 52218dd9d802729d2430cb35057adbc079601278 had to be reverted. --- Project.toml | 2 ++ docs/Manifest.toml | 10 +++++- docs/Project.toml | 1 + src/BangBang.jl | 5 +++ src/setfield.jl | 74 +++++++++++++++++++++++++++++++++++++++++++ test/Manifest.toml | 10 +++++- test/Project.toml | 1 + test/test_aqua.jl | 6 +++- test/test_setfield.jl | 16 ++++++++++ 9 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 src/setfield.jl create mode 100644 test/test_setfield.jl diff --git a/Project.toml b/Project.toml index 06292ae2..e56769a4 100644 --- a/Project.toml +++ b/Project.toml @@ -10,6 +10,7 @@ Future = "9fa8497b-333b-5362-9e8d-4d0656e87820" InitialValues = "22cec73e-a1b8-11e9-2c92-598750a2cf9c" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Requires = "ae029012-a4dd-5104-9daa-d747884805df" +Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" ZygoteRules = "700de1a5-db45-46bc-99cf-38207098b444" @@ -18,6 +19,7 @@ Compat = "2" ConstructionBase = "0.1" InitialValues = "0.2" Requires = "0.5" +Setfield = "0.5.1" Tables = "0.2" ZygoteRules = "0.1, 0.2" julia = "1.0" diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 1b5996e6..ba7012bd 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,7 +1,7 @@ # This file is machine-generated - editing it directly is not advised [[BangBang]] -deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Tables", "ZygoteRules"] +deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Setfield", "Tables", "ZygoteRules"] path = ".." uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" version = "0.3.2-DEV" @@ -197,6 +197,14 @@ uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[Setfield]] +deps = ["ConstructionBase", "MacroTools"] +git-tree-sha1 = "17d7a3334861580d46a4578fb60acefa1376a829" +repo-rev = "master" +repo-url = "https://github.com/jw3126/Setfield.jl.git" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "0.5.0" + [[SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" diff --git a/docs/Project.toml b/docs/Project.toml index a9e0e987..5647e27f 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -3,5 +3,6 @@ BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9" diff --git a/src/BangBang.jl b/src/BangBang.jl index b030d6f6..1a7aaa27 100644 --- a/src/BangBang.jl +++ b/src/BangBang.jl @@ -4,6 +4,7 @@ module BangBang @doc read(joinpath(dirname(@__DIR__), "README.md"), String) BangBang export @!, + @set!!, Empty, append!!, delete!!, @@ -13,6 +14,7 @@ export @!, mul!!, pop!!, popfirst!!, + prefermutation, push!!, pushfirst!!, rmul!!, @@ -45,6 +47,9 @@ include("macro.jl") include("dataframes_impl.jl") include("zygote.jl") +include("setfield.jl") +using .SetfieldImpl: @set!!, prefermutation + function __init__() @require StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" begin include("staticarrays.jl") diff --git a/src/setfield.jl b/src/setfield.jl new file mode 100644 index 00000000..e4304fe7 --- /dev/null +++ b/src/setfield.jl @@ -0,0 +1,74 @@ +module SetfieldImpl + +using ..BangBang: setproperty!!, setindex!! +using Setfield: ComposedLens, + ConstIndexLens, + DynamicIndexLens, + IndexLens, + Lens, + PropertyLens, + Setfield, + set + +struct Lens!!{T <: Lens} <: Lens + lens::T +end + +Setfield.get(obj, lens::Lens!!) = get(obj, lens.lens) + +# Default to immutable: +Setfield.set(obj, lens::Lens!!, value) = + set(obj, lens.lens, value) + +Setfield.set(obj, lens::Lens!!{<:ComposedLens}, value) = + set(obj, Lens!!(lens.lens.outer) ∘ Lens!!(lens.lens.inner), value) + +Setfield.set(obj, ::Lens!!{<:PropertyLens{fieldname}}, value) where fieldname = + setproperty!!(obj, fieldname, value) + +const SupportedIndexLens = Union{ + ConstIndexLens, + DynamicIndexLens, + IndexLens, +} + +indicesfor(lens::IndexLens, _) = lens.indices +indicesfor(::ConstIndexLens{I}, _) where I = I +indicesfor(lens::DynamicIndexLens, obj) = lens.f(obj) + +Setfield.set(obj, lens::Lens!!{<:SupportedIndexLens}, value) = + setindex!!(obj, value, indicesfor(lens, obj)...) + +""" + prefermutation(lens::Lens) :: Lens + +See also [`@set!!`](@ref). +""" +prefermutation(lens::Lens) = Lens!!(lens) + +""" + @set!! assignment + +Like `Setfield.@set`, but prefer mutation if possible. + +# Examples +```jldoctest +julia> using BangBang + +julia> mutable struct Mutable + a + b + end + +julia> x = orig = Mutable((x=Mutable(1, 2), y=3), 4); + +julia> @set!! x.a.x.a = 10; + +julia> @assert x.a.x.a == orig.a.x.a == 10 +``` +""" +macro set!!(ex) + Setfield.setmacro(prefermutation, ex, overwrite=true) +end + +end # module diff --git a/test/Manifest.toml b/test/Manifest.toml index f56a7e52..2b2c6a70 100644 --- a/test/Manifest.toml +++ b/test/Manifest.toml @@ -15,7 +15,7 @@ uuid = "4c88cf16-eb10-579e-8560-4a9242c79595" version = "0.4.0" [[BangBang]] -deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Tables", "ZygoteRules"] +deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Setfield", "Tables", "ZygoteRules"] path = ".." uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" version = "0.3.2-DEV" @@ -301,6 +301,14 @@ uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[Setfield]] +deps = ["ConstructionBase", "MacroTools"] +git-tree-sha1 = "17d7a3334861580d46a4578fb60acefa1376a829" +repo-rev = "master" +repo-url = "https://github.com/jw3126/Setfield.jl.git" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "0.5.0" + [[SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" diff --git a/test/Project.toml b/test/Project.toml index d8b0288b..e5bb3db3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -9,6 +9,7 @@ IRTest = "c9680f28-2a59-47f6-98e4-d41620d42cd2" InitialValues = "22cec73e-a1b8-11e9-2c92-598750a2cf9c" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/test_aqua.jl b/test/test_aqua.jl index 4c17e389..919d6dfc 100644 --- a/test/test_aqua.jl +++ b/test/test_aqua.jl @@ -2,7 +2,11 @@ module TestAqua using Aqua using BangBang +using Setfield -Aqua.test_all(BangBang) +Aqua.test_all( + BangBang; + ambiguities=(exclude=[Base.get, Setfield.set, Setfield.modify],), +) end # module diff --git a/test/test_setfield.jl b/test/test_setfield.jl new file mode 100644 index 00000000..5089a052 --- /dev/null +++ b/test/test_setfield.jl @@ -0,0 +1,16 @@ +module TestSetfield + +include("preamble.jl") + +mutable struct Mutable + a + b +end + +@testset begin + x = orig = Mutable((x=Mutable(1, 2), y=3), 4); + @set!! x.a.x.a = 10; + @test x.a.x.a == orig.a.x.a == 10 +end + +end # module