Skip to content

Commit 039c25f

Browse files
authored
Merge pull request #13 from JuliaObjects/docs
docs
2 parents 2cf14f8 + 1bbe6a9 commit 039c25f

File tree

7 files changed

+241
-40
lines changed

7 files changed

+241
-40
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ Updating immutable data was never easier:
1717
using Accessors
1818
@set obj.a.b.c = d
1919
```
20-
For more information, see [the documentation](https://juliaobjects.github.io/Accessors.jl/dev/intro/) and/or watch this video:
20+
To get started, see [this tutorial](https://juliaobjects.github.io/Accessors.jl/stable/getting_started/) and/or watch this video:
2121

22-
[![JuliaCon2020 Changing the immutable](https://img.youtube.com/vi/vkAOYeTpLg0/0.jpg)](https://youtu.be/vkAOYeTpLg0 "Changing the immutable")
22+
[![JuliaCon2020 Changing the immutable](https://img.youtube.com/vi/vkAOYeTpLg0/0.jpg)](https://youtu.be/vkAOYeTpLg0 "Changing the immutable")

docs/make.jl

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,30 @@ mkpath(outputdir)
66
mkpath(joinpath(outputdir, "examples"))
77
for filename in readdir(inputdir)
88
inpath = joinpath(inputdir, filename)
9-
cp(inpath, joinpath(outputdir, "examples", filename), force=true)
10-
Literate.markdown(inpath, outputdir; documenter=true)
9+
cp(inpath, joinpath(outputdir, "examples", filename), force = true)
10+
Literate.markdown(inpath, outputdir; documenter = true)
1111
end
12+
cp(joinpath(@__DIR__, "..", "README.md"), joinpath(@__DIR__, "src", "index.md"))
1213

1314
makedocs(
14-
modules = [Accessors],
15-
sitename = "Accessors.jl",
16-
pages = [
17-
"Introduction" => "intro.md",
18-
"Lenses" => "lenses.md",
19-
"Docstrings" => "index.md",
15+
modules = [Accessors],
16+
sitename = "Accessors.jl",
17+
pages = [
18+
"Home" => "index.md",
19+
"Tutorials" => ["Getting started" => "getting_started.md",],
20+
"Explanation" => ["Lenses" => "lenses.md",],
21+
"Reference" => ["Docstrings" => "docstrings.md"],
22+
"How-to guides" => [
23+
"Custom Optics" => "examples/custom_optics.md",
2024
"Custom Macros" => "examples/custom_macros.md",
21-
"Experimental features" => "examples/specter.md",
22-
hide("internals.md"),
23-
],
24-
strict = true, # to exit with non-zero code on error
25-
)
25+
],
26+
hide("examples/specter.md"),
27+
hide("internals.md"),
28+
],
29+
strict = true, # to exit with non-zero code on error
30+
)
2631

27-
deploydocs(
32+
deploydocs(;
2833
repo = "github.com/JuliaObjects/Accessors.jl.git",
34+
push_preview=true
2935
)
File renamed without changes.

docs/src/getting_started.jl

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# # Getting started
2+
Say you have a `NamedTuple` and you want to update it:
3+
```jldoctest getting_started
4+
julia> x = (greeting="Hello", name="World")
5+
(greeting = "Hello", name = "World")
6+
7+
julia> x.greeting = "Hi"
8+
ERROR: setfield! immutable struct of type NamedTuple cannot be changed
9+
[...]
10+
```
11+
This fails, because named tuples are immutable. Instead you can use Accessors to carry out the update:
12+
13+
```jldoctest getting_started
14+
julia> using Accessors
15+
16+
julia> @set x.greeting = "Hi"
17+
(greeting = "Hi", name = "World")
18+
19+
julia> x # still the same. Accessors did not overwrite x, it just created an updated copy
20+
(greeting = "Hello", name = "World")
21+
22+
julia> x_new = @set x.greeting = "Hi" # typically you will assign a name to the updated copy
23+
(greeting = "Hi", name = "World")
24+
```
25+
Accessors.jl does not only support `NamedTuple`, but arbitrary structs and nested updates.
26+
```jldoctest getting_started
27+
julia> struct HelloWorld
28+
greeting::String
29+
name::String
30+
end
31+
32+
julia> x = HelloWorld("hi", "World")
33+
HelloWorld("hi", "World")
34+
35+
julia> @set x.name = "Accessors" # update a struct
36+
HelloWorld("hi", "Accessors")
37+
38+
julia> x = (a=1, b=(c=3, d=4))
39+
(a = 1, b = (c = 3, d = 4))
40+
41+
julia> @set x.b.c = 10 # nested update
42+
(a = 1, b = (c = 10, d = 4))
43+
```
44+
Accessors.jl does not only support updates of properties, but also index updates.
45+
```jldoctest getting_started
46+
julia> x = (10,20,21)
47+
(10, 20, 21)
48+
49+
julia> @set x[3] = 30
50+
(10, 20, 30)
51+
```
52+
In fact Accessors.jl supports many more notions of update:
53+
```jldoctest getting_started
54+
julia> x = [1,2,3];
55+
56+
julia> x_new = @set eltype(x) = UInt8;
57+
58+
julia> @show x_new;
59+
x_new = UInt8[0x01, 0x02, 0x03]
60+
```
61+
Accessors.jl is very composable, which means different updates can be nested and combined.
62+
```jldoctest getting_started
63+
julia> data = (a = (b = (1,2),), c=3)
64+
(a = (b = (1, 2),), c = 3)
65+
66+
julia> @set data.a.b[end] = 20
67+
(a = (b = (1, 20),), c = 3)
68+
69+
julia> @set splitext("some_file.py")[2] = ".jl"
70+
"some_file.jl"
71+
```

docs/src/getting_started.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Getting started
2+
Say you have a `NamedTuple` and you want to update it:
3+
```jldoctest getting_started
4+
julia> x = (greeting="Hello", name="World")
5+
(greeting = "Hello", name = "World")
6+
7+
julia> x.greeting = "Hi"
8+
ERROR: setfield! immutable struct of type NamedTuple cannot be changed
9+
[...]
10+
```
11+
This fails, because named tuples are immutable. Instead you can use Accessors to carry out the update:
12+
13+
```jldoctest getting_started
14+
julia> using Accessors
15+
16+
julia> @set x.greeting = "Hi"
17+
(greeting = "Hi", name = "World")
18+
19+
julia> x # still the same. Accessors did not overwrite x, it just created an updated copy
20+
(greeting = "Hello", name = "World")
21+
22+
julia> x_new = @set x.greeting = "Hi" # typically you will assign a name to the updated copy
23+
(greeting = "Hi", name = "World")
24+
```
25+
Accessors.jl does not only support `NamedTuple`, but arbitrary structs and nested updates.
26+
```jldoctest getting_started
27+
julia> struct HelloWorld
28+
greeting::String
29+
name::String
30+
end
31+
32+
julia> x = HelloWorld("hi", "World")
33+
HelloWorld("hi", "World")
34+
35+
julia> @set x.name = "Accessors" # update a struct
36+
HelloWorld("hi", "Accessors")
37+
38+
julia> x = (a=1, b=(c=3, d=4))
39+
(a = 1, b = (c = 3, d = 4))
40+
41+
julia> @set x.b.c = 10 # nested update
42+
(a = 1, b = (c = 10, d = 4))
43+
```
44+
Accessors.jl does not only support updates of properties, but also index updates.
45+
```jldoctest getting_started
46+
julia> x = (10,20,21)
47+
(10, 20, 21)
48+
49+
julia> @set x[3] = 30
50+
(10, 20, 30)
51+
```
52+
In fact Accessors.jl supports many more notions of update:
53+
```jldoctest getting_started
54+
julia> x = [1,2,3];
55+
56+
julia> x_new = @set eltype(x) = UInt8;
57+
58+
julia> @show x_new;
59+
x_new = UInt8[0x01, 0x02, 0x03]
60+
```
61+
Accessors.jl is very composable, which means different updates can be nested and combined.
62+
```jldoctest getting_started
63+
julia> data = (a = (b = (1,2),), c=3)
64+
(a = (b = (1, 2),), c = 3)
65+
66+
julia> @set data.a.b[end] = 20
67+
(a = (b = (1, 20),), c = 3)
68+
69+
julia> @set splitext("some_file.py")[2] = ".jl"
70+
"some_file.jl"
71+
```

examples/custom_optics.jl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# # Custom optics
2+
#
3+
# This guide demonstrates how to implement new kinds of optics.
4+
# There are multiple possibilities, the most straight forward one is function lenses
5+
#
6+
# ### Function lenses
7+
# Say we are dealing with points in the plane and polar coordinates are of interest:
8+
9+
struct Point
10+
x::Float64
11+
y::Float64
12+
end
13+
14+
function Base.isapprox(pt1::Point, pt2::Point; kw...)
15+
return isapprox([pt1.x,pt1.y], [pt2.x, pt2.y]; kw...)
16+
end
17+
18+
function polar(pt::Point)
19+
r = sqrt(pt.x^2 + pt.y^2)
20+
θ = atan(pt.y,pt.x)
21+
return (r=r, θ=θ)
22+
end
23+
24+
function Point_from_polar(rθ)
25+
r, θ =
26+
x = cos(θ)*r
27+
y = sin(θ)*r
28+
return Point(x,y)
29+
end
30+
31+
# It would certainly be ergonomic to do things like
32+
# `@set polar(pt) = ...`
33+
# `@set polar(pt).θ = ...`
34+
# `@set polar(pt).r = 1`
35+
# To enable this, a function lens can be implemented:
36+
#
37+
using Accessors
38+
using Test
39+
Accessors.set(pt, ::typeof(polar), rθ) = Point_from_polar(rθ)
40+
#
41+
# And now it is possible to do
42+
pt = Point(2,0)
43+
pt2 = @set polar(pt).r = 5
44+
@test pt2 Point(5,0)
45+
46+
pt3 = @set polar(pt).θ = π/2
47+
@test pt3 Point(0, 2)
48+
49+
# ### Modify based optics
50+
#
51+
# Say we have a `Dict` and we want to update all of its keys. This can be done as follows:
52+
function mapkeys(f, d::Dict)
53+
return Dict(f(k) => v for (k,v) in pairs(d))
54+
end
55+
# Lets make this more ergonomic by defining an optic for it
56+
#
57+
using Accessors
58+
struct Keys end
59+
Accessors.OpticStyle(::Type{Keys}) = ModifyBased()
60+
Accessors.modify(f, obj, ::Keys) = mapkeys(f, obj)
61+
# It can be used as follows:
62+
obj = Dict("A" =>1, "B" => 2, "C" => 3)
63+
obj2 = @modify(lowercase, obj |> Keys())
64+
@test obj2 == Dict("a" =>1, "b" => 2, "c" => 3)

examples/specter.jl

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# # Traversal
2-
# These examples are taken from the README.md of the specter clojure library.
2+
#
3+
# This code demonstrates how to use and extend advanced optics.
4+
# The examples are taken from the README.md of the specter clojure library.
35
# Many of the features here are experimental, please consult the docstrings of the
46
# involved optics.
57

@@ -9,30 +11,25 @@ using Accessors
911
import Accessors: modify, OpticStyle
1012
using Accessors: ModifyBased, SetBased, setindex
1113

14+
# ### Increment all even numbers
15+
# We have the following data and the goal is to increment all nested even numbers.
16+
data = (a = [(aa=1, bb=2), (cc=3,)], b = [(dd=4,)])
17+
# To acomplish this, we define a new optic `Vals`.
1218
function mapvals(f, d)
1319
Dict(k => f(v) for (k,v) in pairs(d))
1420
end
1521

1622
mapvals(f, nt::NamedTuple) = map(f, nt)
17-
function mapkeys(f, d)
18-
Dict(f(k) => v for (k,v) in pairs(d))
19-
end
20-
21-
function mapkeys(f, nt::NamedTuple)
22-
kw = map(pairs(nt)) do (key, val)
23-
f(key) => val
24-
end
25-
(;kw...)
26-
end
27-
28-
struct Keys end
29-
OpticStyle(::Type{Keys}) = ModifyBased()
30-
modify(f, obj, ::Keys) = mapkeys(f, obj)
3123

3224
struct Vals end
3325
OpticStyle(::Type{Vals}) = ModifyBased()
3426
modify(f, obj, ::Vals) = mapvals(f, obj)
3527

28+
# Now we can increment as follows:
29+
out = @set data |> Vals() |> Elements() |> Vals() |> If(iseven) += 1
30+
31+
@test out == (a = [(aa = 1, bb = 3), (cc = 3,)], b = [(dd = 5,)])
32+
3633
struct Filter{F}
3734
keep_condition::F
3835
end
@@ -51,18 +48,10 @@ function modify(f, obj, optic::Filter)
5148
setindex(obj, vals, inds)
5249
end
5350

54-
# ### Increment all even numbers
55-
data = (a = [(aa=1, bb=2), (cc=3,)], b = [(dd=4,)])
56-
57-
out = @set data |> Vals() |> Elements() |> Vals() |> If(iseven) += 1
58-
59-
@test out == (a = [(aa = 1, bb = 3), (cc = 3,)], b = [(dd = 5,)])
6051

6152
# ### Append to nested vector
6253
data = (a = 1:3,)
63-
64-
optic = @optic _.a
65-
out = modify(v -> vcat(v, [4,5]), data, optic)
54+
out = @modify(v -> vcat(v, [4,5]), data.a)
6655

6756
@test out == (a = [1,2,3,4,5],)
6857

0 commit comments

Comments
 (0)