Functors.jl icon indicating copy to clipboard operation
Functors.jl copied to clipboard

`@functor` is confused about properties vs fields

Open mcabbott opened this issue 3 years ago • 5 comments
trafficstars

It was noticed in https://github.com/FluxML/Flux.jl/issues/2107 that Functors.jl + ProtoStruct.jl doesn't work, as Functors uses fieldnames + getproperty, which is overloaded by ProtoStruct.jl.

https://github.com/FluxML/Functors.jl/blob/v0.3.0/src/functor.jl#L11-L16

This is a bug, Functors should use getfield to be consistent. (Possibly the code was written before getproperty existed?)

mcabbott avatar Nov 10 '22 20:11 mcabbott

With the reversion of #48, I guess our second attempt would be to try to use all properties? It won't be type stable for most types that define custom getproperty overloads, but playing around with https://github.com/JuliaObjects/ConstructionBase.jl/blob/40800e17ef953c5cccaeef2d1d651346cacc5014/src/ConstructionBase.jl#L75-L78 suggests it should be for those which don't.

ToucheSir avatar Nov 16 '22 03:11 ToucheSir

So the problem in #48 is Tangent, which Optimisers.jl wants to access via getproperty. Not sure that's a great design but it won't change soon.

For Functors to use properties-not-fields everywhere, I think it would have to be functor(guide, target) rather than functor(::Type{Guide}, target). I haven't thought through what else this would change.

mcabbott avatar Nov 16 '22 03:11 mcabbott

For Functors to use properties-not-fields everywhere, I think it would have to be functor(guide, target) rather than functor(::Type{Guide}, target). I haven't thought through what else this would change.

In my refactor with ConstructionBase, I tried making children and rebuilder (thing that returns re) the foundation instead of functor. In this situation you don't have access to target and guide together, so you need access to the guide itself to close over the leaf nodes.

darsnack avatar Nov 16 '22 14:11 darsnack

One barrier to properties-everywhere is that Zygote uses fields:

julia> using ProtoStructs, Zygote

julia> @proto struct Mine
         num::Float64
       end

julia> gradient(x -> x.num^2, Mine(3.0))[1]
(properties = (num = 6.0,),)

julia> propertynames(Mine(3.0))
(:num,)

mcabbott avatar Nov 21 '22 16:11 mcabbott

And because we return plain NamedTuples instead of e.g. Tangents, we don't even know whether that was meant to be a ProtoStruct. Even ChainRules assumes fields are being used in a few places—canonicalize comes to mind.

ToucheSir avatar Nov 22 '22 01:11 ToucheSir