Skip to content

Commit 718e26c

Browse files
committed
improve inference of mapproperties
1 parent e251609 commit 718e26c

File tree

3 files changed

+54
-13
lines changed

3 files changed

+54
-13
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1515
[compat]
1616
Compat = "3.18"
1717
CompositionsBase = "0.1"
18-
ConstructionBase = "0.1, 1.0"
18+
ConstructionBase = "1.2"
1919
MacroTools = "0.4.4, 0.5"
2020
Requires = "0.5, 1.0"
2121
StaticNumbers = "0.3"

src/optics.jl

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -232,21 +232,25 @@ julia> obj = (a=1, b=2);
232232
julia> Accessors.mapproperties(x -> x+1, obj)
233233
(a = 2, b = 3)
234234
```
235+
236+
# Implementation
237+
238+
This function should not be overloaded directly. Instead both of
239+
* `ConstructionBase.getproperties`
240+
* `ConstructionBase.setproperties`
241+
should be overloaded.
235242
$EXPERIMENTAL
236243
"""
244+
function mapproperties end
245+
246+
function mapproperties(f, nt::NamedTuple)
247+
map(f,nt)
248+
end
249+
237250
function mapproperties(f, obj)
238-
# TODO move this helper elsewhere?
239-
# TODO should we use a generated function based on fieldnames?
240-
pnames = propertynames(obj)
241-
if isempty(pnames)
242-
return obj
243-
else
244-
ctor = constructorof(typeof(obj))
245-
new_props = map(pnames) do p
246-
f(getproperty(obj, p))
247-
end
248-
return ctor(new_props...)
249-
end
251+
nt = getproperties(obj)
252+
patch = mapproperties(f, nt)
253+
return setproperties(obj, patch)
250254
end
251255

252256
"""

test/test_optics.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,48 @@
11
module TestOptics
22

33
using Accessors
4+
using Accessors: mapproperties
45
using Test
6+
import ConstructionBase
7+
8+
@testset "mapproperties" begin
9+
res = @inferred mapproperties(x->2x, (a=1, b=2))
10+
@test res === (a=2, b=4)
11+
@test NamedTuple() === @inferred mapproperties(cos, NamedTuple())
12+
struct S{A,B}
13+
a::A
14+
b::B
15+
end
16+
res = @inferred mapproperties(x->2x, S(1, 2.0))
17+
@test res === S(2, 4.0)
18+
end
519

620
@testset "Properties" begin
721
pt = (x=1, y=2, z=3)
822
@test (x=0, y=1, z=2) === @set pt |> Properties() -= 1
23+
@inferred modify(x->x-1, pt, Properties())
24+
25+
# custom struct
26+
struct Point{X,Y,Z}
27+
x::X; y::Y; z::Z
28+
end
29+
pt = Point(1f0, 2e0, 3)
30+
pt2 = @inferred modify(x->2x, pt, Properties())
31+
@test pt2 === Point(2f0, 4e0, 6)
32+
33+
# overloading
34+
struct AB
35+
a::Int
36+
b::Int
37+
_checksum::Int
38+
AB(a,b) = new(a,b,a+b)
39+
end
40+
41+
ConstructionBase.getproperties(o::AB) = (a=o.a, b=o.b)
42+
ConstructionBase.setproperties(o::AB, patch::NamedTuple) = AB(patch.a, patch.b)
43+
ab = AB(1,2)
44+
ab2 = @inferred mapproperties(x -> 2x, ab)
45+
@test ab2 === AB(2,4)
946
end
1047

1148
@testset "Elements" begin

0 commit comments

Comments
 (0)