Skip to content

Commit

Permalink
Merge pull request JuliaObjects#24 from JuliaObjects/properties
Browse files Browse the repository at this point in the history
Implement mapproperties in terms of setproperties and getproperties
  • Loading branch information
jw3126 authored Jun 11, 2021
2 parents a0a488c + a5301d8 commit b541bcc
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[compat]
Compat = "3.18"
CompositionsBase = "0.1"
ConstructionBase = "0.1, 1.0"
ConstructionBase = "1.2"
MacroTools = "0.4.4, 0.5"
Requires = "0.5, 1.0"
StaticNumbers = "0.3"
Expand Down
28 changes: 16 additions & 12 deletions src/optics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -232,21 +232,25 @@ julia> obj = (a=1, b=2);
julia> Accessors.mapproperties(x -> x+1, obj)
(a = 2, b = 3)
```
# Implementation
This function should not be overloaded directly. Instead both of
* `ConstructionBase.getproperties`
* `ConstructionBase.setproperties`
should be overloaded.
$EXPERIMENTAL
"""
function mapproperties end

function mapproperties(f, nt::NamedTuple)
map(f,nt)
end

function mapproperties(f, obj)
# TODO move this helper elsewhere?
# TODO should we use a generated function based on fieldnames?
pnames = propertynames(obj)
if isempty(pnames)
return obj
else
ctor = constructorof(typeof(obj))
new_props = map(pnames) do p
f(getproperty(obj, p))
end
return ctor(new_props...)
end
nt = getproperties(obj)
patch = mapproperties(f, nt)
return setproperties(obj, patch)
end

"""
Expand Down
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import PerformanceTestTools
import Accessors

using Documenter: doctest
if VERSION >= v"1.5" # ⨟ needs to be defined
if VERSION == v"1.6"
# ⨟ needs to be defined
doctest(Accessors)
else
@info "Skipping doctests, on old VERSION = $VERSION"
Expand Down
4 changes: 3 additions & 1 deletion test/test_examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ using Test
dir = joinpath("..", "examples")
@testset "example $filename" for filename in readdir(dir)
path = joinpath(dir, filename)
include(path)
@eval module $(Symbol("TestExample_$filename"))
include($path)
end
end
end#module
37 changes: 37 additions & 0 deletions test/test_optics.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
module TestOptics

using Accessors
using Accessors: mapproperties
using Test
import ConstructionBase

@testset "mapproperties" begin
res = @inferred mapproperties(x->2x, (a=1, b=2))
@test res === (a=2, b=4)
@test NamedTuple() === @inferred mapproperties(cos, NamedTuple())
struct S{A,B}
a::A
b::B
end
res = @inferred mapproperties(x->2x, S(1, 2.0))
@test res === S(2, 4.0)

# overloading
struct AB
a::Int
b::Int
_checksum::UInt
AB(a,b) = new(a,b,hash(a,hash(b)))
end

ConstructionBase.getproperties(o::AB) = (a=o.a, b=o.b)
ConstructionBase.setproperties(o::AB, patch::NamedTuple) = AB(patch.a, patch.b)
ab = AB(1,2)
ab2 = @inferred mapproperties(x -> 2x, ab)
@test ab2 === AB(2,4)
end

@testset "Properties" begin
pt = (x=1, y=2, z=3)
@test (x=0, y=1, z=2) === @set pt |> Properties() -= 1
@inferred modify(x->x-1, pt, Properties())

# custom struct
struct Point{X,Y,Z}
x::X; y::Y; z::Z
end
pt = Point(1f0, 2e0, 3)
pt2 = @inferred modify(x->2x, pt, Properties())
@test pt2 === Point(2f0, 4e0, 6)
end

@testset "Elements" begin
Expand Down

0 comments on commit b541bcc

Please sign in to comment.