Julia implementation of Lens using generated function! This is super cool
Though I suppose the type stability is not coming from lens but rather because you are using the default constructor (or the order of arguments to the constructor has to match with that of field).
what happens when the struct has one (or two) primitive fields and ten of these are allocated and stuffed next to each other in sequence forming an immediate, not indirect manner of indexing and the first field of the sixth struct has its value changed?
I think there is no way to get full type stability here without generated functions at some point.
In order to manipulate fields, you need to process fieldnames(typeof(obj)) in one way or another. Without generated functions, this looks a priori like an arbitrary vector of symbols to the compiler. The compiler would need to
Figure out that its safe to “evaluate” this vector at compile time
Fully unroll the loop/recursion you use to process the fields
Constant propagate all the field symbols
I am not a compiler guy, but to me this seems incredibly hard.
You may want to change the initial condition u0 type from Vector to Matrix, or regular array to static array or GPU array, etc. This is going to change the type parameters of the problem type.
Okay I think we can do that. We need ways to hook into @set. I think
@set obj.x = 1.0
should call setproperty(b, Val{:x},1.0) and then the user could overload setproperty to get any kind of interesting behaviour. The default implementation of setproperty should be something like
destructure obj into properties
manipulate properties
construct new_obj from typeof(obj) and properties
Each step should be overloadable. 3) is where you could hook in your
Reconstructables.constructor_of(::Type{<: B{T}}) where T = B{T}
Hmm… In that case, do you need to call setproperty from set(l::FieldLens{field}, ...) which is a generated function? If so, my understanding is that it is not going to work since the codegen part of the generated function has to be pure. Allowing users to “register” their definition in setproperty is not pure.
function set(l::Fieldlens, obj, val)
setproperty(obj, field(l), val)
end
function setproperty(obj, field, val) # field is say a Val
fields = replace_field(obj, field, val) # fields is a tuple
constructor_of(obj)(fields...)
end
@generated replace_field...
For the setter, generated functions are probably needed, although in Julia 0.7 maybe they could be avoided by recursions. But I’m not sure whether that would be of any benefit.
It should be noted that keyword arguments are only an issue on v0.6, so there’s no reason to make such a big deal about that. Generated functions are not statically compilable, so that would be a big deal down the road.
But it would be nice to use this pattern also without having keyword constructors. At least for as long as keyword constructors are not defined automatically.
I guess in the long run the problem will be solved in Base anyway. Keyword constructors are nice for large structs. However they are only an option, if you own the struct. Adding one to foreign structs is annoying and leads to bugs. Also not every Point(x,y) should have its own keyword constructor.
Oops. Forget what I said You can call setproperty inside the code generated by a generated function. You wouldn’t call it inside the codegen code part so it’s not a problem.