Handling structs without the default constructor via `Expr(:new,...` ?
In FiniteDifferences, this PR allowed custom constructors to be handled as part of to_vec: https://github.com/JuliaDiff/FiniteDifferences.jl/pull/181 here
However iiuc construct in CRC here doesn't handle this. In this case however, the constructor requirements are mentioned in the docstring.
However, tests in CRTU will construct the new type as part of the call to FiniteDifferences but then fail later as the constructor is not found in the call to construct.
Allowing this here, or otherwise, it would be easier if these behaviours were consistent in terms of custom types as part of the CTRU testing pipeline.
EDIT: MWE:
struct CustomType{T}
x
function CustomType(X::T) where {T}
new{T}(X)
end
end
# This misses the constructor:
CustomType{Int64}(5) # ERROR: MethodError: no method matching CustomType{Int64}(::Int64)
val = CustomType(5) # CustomType{Int64}(5)
diff = Tangent{typeof(val)}(x = 5) # Tangent{CustomType{Int64}}(x = 5,)
val + diff #ERROR: MethodError: no method matching CustomType{Int64}(::Int64)
# FiniteDifferences handles this in the vec to x:
to_vec(CustomType(5))[2]([6.0]) #CustomType{Int64}(6.0)
Defining CustomType{T}(x) where {T} = CustomType(x) allows this pass.
cc @mzgubic
Oh, that's interesting. Is the suggestion that we implement the same hack in construct? (We should probably consider a hack in CRC more carefully than in FD).
Could you provide an MWE where this fails to see if there is a way around it?
Is the suggestion that we implement the same hack in construct? (We should probably consider a hack in CRC more carefully than in FD).
Agree with this. I wasn't going so far as to suggest this because of the this reason. However from a user perspective, I just found when working with a custom type, the constructor issue re-emerged later when testing because of, as it turns out, the above reason (if i've diagnosed it correctly).
In general, having these constructors would be prefereable rather than these workarounds. OTOH, this may make working with custom types a bit easier.
I've added a MWE.
Yeah, we could do this. We have thought about doing this before. It does mean we could violate some assumptions.
My point on a unit circle example is one.
struct CirclePoint
x::Float64
y::Float64
CirclePoint(angle) = new(cos(angle), sin(angle))
end
foo(c::CiclePoint) = 2c.x + c.y
dc = rrule(foo, CirclePoint(pi/2))[2](1.0)
dc == Tangent{CirclePoint}(x=2, 1)
CirclePoint(pi/2) + dc == some point that is not on the unit circle