ANN: Setfield.jl Yet another package for working with immutables

Okay I was not suggesting to apply this manually to every type under the sun. That is indeed error prone and ugly and what not. Also I was not suggesting this constructor for human use in types with lots of fields.

I think most types should have 0-3 fields and no custom constructor at all. If I apply this pattern I usually use a macro.

You are right, we need a standard constructor for the machine, for packages like Reconstructables.jl and Setfield.jl to work.
I think the positional constructor has one killer advantage. Its the default. This means it automatically works for most types. Especially useful for types defined in packages you don’t own.

I agree that the positional constructor has a huge advantage in that it is the (only) default at the moment. But keyworded constructor may have language support in the future as well:
https://github.com/JuliaLang/julia/issues/10146

Anyway, now that constructor lookup can be customized via Setfield.constructor_of, I guess I can do whatever I want to do on top of Setfield.jl which is great!

I started liking lens-based approach! Now that lens can change type parameters, we have “differentiable lens”!

using ForwardDiff
using Parameters: @with_kw, @unpack
using Setfield: @lens, Lens, set, get
import Setfield

@with_kw struct Coordinate{X, Y, Z}
    x::X = 1.0
    y::Y = 1.0
    z::Z = 1.0
end

# Since set called via ForwardDiff would specify a dual number, we
# have to ignore type parameters:
Setfield.constructor_of(::Type{<: Coordinate}) = Coordinate

function f(c::Coordinate)
    @unpack x, y, z = c
    return x^2 + y^2 + z^2
end

derivative(f, at, wrt::Lens) =
    ForwardDiff.derivative(
        (x) -> f(set(wrt, at, x)),
        get(wrt, at))

@assert derivative(f, Coordinate(x=1.0), @lens _.x) ≈ 2
@assert derivative(f, Coordinate(x=2.0), @lens _.x) ≈ 4

This is actually super useful for building something like bifurcation analysis tool where you need a generic way to let user specify a few parameters in a differentiable manner.

2 Likes

I had still doubts, that allowing to change the type was a good idea, but this example convinces me. Nice!

Glad that you like the example :slight_smile: