GithubHelp home page GithubHelp logo

Comments (7)

jw3126 avatar jw3126 commented on May 28, 2024

One way to implement something like this would be as follows:

example(::Type{<:Complex}) = 0 + 0im
example(::Type{<:Gaussian}) = ...

and then @assemble Gaussian A(_) = 1, fwhm(_) = 1 will expand to

obj = example(Gaussian)
obj = @set A(obj) = 1
obj = @set fwhm(obj) = 1

The disadvantages of this approach would be that it is tricky to make it work for Vector, Tuple, Set.... Also
it is only a sane operation, if alle the specified lenses commute and give a complete description of an object.
Performance might also be bad in some cases.
Advantages would be that it works for any lenses and any combination of lenses. It does not make defining new lenses any harder.

from accessors.jl.

aplavin avatar aplavin commented on May 28, 2024

Sometimes it's not possible to define a setter for a function, but it still can participate in assembly. For example, which parameter should @set area(gaussian) = 1 change - size or amplitude? But assembly from a size and area is unambiguous.

Potentially,

obj = example(Gaussian)
obj = @set A(obj) = 1
obj = @set fwhm(obj) = 1

could be a fallback @assembly implementation, but I think it is error-prone: very easy to provide a dependent/inconsistent/incomplete set of optics, and get no error.

I imagine a separate function that needs to be overloaded explicitly, like

assemble(::Type{Gaussian}, ::Pair{typeof(area)}, ::Pair{typeof(fwhm)})

So, only specific optic combinations would be supported, that are correct and unambiguous.

Usage of this function interface without @assemble macro:

assemble(Gaussian, area => 1, fwhm => 2)
assemble(NamedTuple, PropertyLens(:a) => assemble(...), PropertyLens(:b) => assemble(...))

Tangentially to @assemble, a feature like set g.size so that area(g) = 10 would also be useful sometimes...

from accessors.jl.

jw3126 avatar jw3126 commented on May 28, 2024

I think a good alternative would be to just define some constructors using say KeywordDispatch.jl.
I think the composability of lenses is not very useful here, so keywords are a simpler solution I would personally prefer

@kwdispatch Gaussian()
@kwmethod Gaussian(;A, area) = ...
@kwmethod Gaussian(;A, fwhm) = ...
@kwmethod Gaussian(;area, fwhm) = ...

Although this does not work well with types you don't own like Complex. If this is important another way would be:

@kwdispatch assemble(::Type{Complex})
@kwmethod(::Type{Complex};real, imag)
@kwmethod(::Type{Complex};polar, abs)

from accessors.jl.

aplavin avatar aplavin commented on May 28, 2024

I think the composability of lenses is not very useful here

Totally agree, I also noticed that!

keywords are a simpler solution I would personally prefer

Keywords are indeed conceptually simpler, even when using an extra "magic" package for dispatch.
However, with optics you can also do more advanced assembly, like

@assemble PowerLawSpectrum  flux(_, 5u"GHz") = 1u"Jy", flux(_, 10u"GHz") = 0.5u"Jy"

When one has a hammer, everything looks suspiciously like nails :)

Anyway, this assembly function doesn't need to be in Accessors because it's only loosely related with primary lens usage patterns. Even more, it's likely better to separate less-stable stuff not to hinder future development. I think, I'll prototype it in AccessorsExtra at some point, and see over time if it's actually useful.

from accessors.jl.

aplavin avatar aplavin commented on May 28, 2024

This function is now available under the name construct in AccessorsExtra. Optic composability doesn't bring much here indeed, but still I find this interface useful for constructing complex structs.

from accessors.jl.

aplavin avatar aplavin commented on May 28, 2024

Just a small update after some time using and extending this construct functionality. I find it quite useful, even in a wider range of situations than expected.
Highlights:

  • Original goal, simply as an interface to construct complicated structs with multiple alternative sets of required parameters. Type author needs to define each combination separately, and then stuff like this works:
construct(Gaussian, area => 1, fwhm => 2)
construct(PowerLawSpectrum, (@o flux(_, 5u"GHz")) => 1u"Jy", (@o flux(_, 10u"GHz")) = 0.5u"Jy")
  • Invertible functions can be composed freely, they are automatically handled by the construct machinery. Actual examples:
julia> construct(NamedTuple, (@o _.a) => 2,  (@o log10(_.b)) => 3)
(a = 2, b = 1000.0)

julia> construct(Complex, abs => 3, rad2deg  angle => 45)
2.121320343559643 + 2.1213203435596424im
  • For some common cases, default types are defined so that this works without manually specifying types – and works recursively:
julia> construct((@o _[1]) => 2,  (@o log10(_[2])) => 3)
(2, 1000.0)

julia> construct((@o _.a) => 2,  (@o log10(_.b)) => 3)
(a = 2, b = 1000.0)

julia> construct((@o _[1].a) => 2,  (@o log10(_[2].b)) => 3, (@o _[2].c) => "")
((a = 2,), (b = 1000.0, c = ""))

This actually works as a right inverse for supported optics:

julia> rinverse(f) = x -> construct(f => x)

julia> rinverse(@o _.a)(5)
(a = 5,)

julia> rinverse(@o log10(_.a))(2)
(a = 100.0,)

Together, these features make construct very nice when one specifies optics and their target values, but manually creating a "base" object (that would work with set()) is not convenient. For example, in AccessibleOptimization, one defines the optimization parameters like

OptArgs(
	(@o _.comps[1].shift) => 0..10.,
	(@o _.comps[1].scale) => 0.3..10.,
        ...
)

and then can provide a "base" object or omit it. If omitted, construct() is used, otherwise regular set() calls.

So, function composability turned out to be actually relevant for construct as well (I didn't really expect that). But yeah this is probably a more niche case than those targeted by Accessors itself.

from accessors.jl.

jw3126 avatar jw3126 commented on May 28, 2024

Thanks a lot for the update!

from accessors.jl.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.