Custom optics
This guide demonstrates how to implement new kinds of optics. There are multiple possibilities, the most straight forward one is function lenses
Function lenses
Say we are dealing with points in the plane and polar coordinates are of interest:
struct Point
x::Float64
y::Float64
end
function Base.isapprox(pt1::Point, pt2::Point; kw...)
return isapprox([pt1.x,pt1.y], [pt2.x, pt2.y]; kw...)
end
function polar(pt::Point)
r = sqrt(pt.x^2 + pt.y^2)
θ = atan(pt.y,pt.x)
return (r=r, θ=θ)
end
function Point_from_polar(rθ)
r, θ = rθ
x = cos(θ)*r
y = sin(θ)*r
return Point(x,y)
end
Point_from_polar (generic function with 1 method)
It would certainly be ergonomic to do things like @set polar(pt) = ...
@set polar(pt).θ = ...
@set polar(pt).r = 1
To enable this, a function lens can be implemented:
using Accessors
using Test
Accessors.set(pt, ::typeof(polar), rθ) = Point_from_polar(rθ)
And now it is possible to do
pt = Point(2,0)
pt2 = @set polar(pt).r = 5
@test pt2 ≈ Point(5,0)
pt3 = @set polar(pt).θ = π/2
@test pt3 ≈ Point(0, 2)
Test Passed
Expression: pt3 ≈ Point(0, 2)
Evaluated: Main.Point(1.2246467991473532e-16, 2.0) ≈ Main.Point(0.0, 2.0)
Modify based optics
Say we have a Dict
and we want to update all of its keys. This can be done as follows:
function mapkeys(f, d::Dict)
return Dict(f(k) => v for (k,v) in pairs(d))
end
mapkeys (generic function with 1 method)
Lets make this more ergonomic by defining an optic for it
using Accessors
struct Keys end
Accessors.OpticStyle(::Type{Keys}) = ModifyBased()
Accessors.modify(f, obj, ::Keys) = mapkeys(f, obj)
It can be used as follows:
obj = Dict("A" =>1, "B" => 2, "C" => 3)
obj2 = @modify(lowercase, obj |> Keys())
@test obj2 == Dict("a" =>1, "b" => 2, "c" => 3)
Test Passed
Expression: obj2 == Dict("a" => 1, "b" => 2, "c" => 3)
Evaluated: Dict("c" => 3, "b" => 2, "a" => 1) == Dict("c" => 3, "b" => 2, "a" => 1)
This page was generated using Literate.jl.