Skip to content

Conversation

@mzgubic
Copy link
Member

@mzgubic mzgubic commented Jul 16, 2021

Before we would see something like:

julia> cca = CustomConstructorArray(rand(2, 3));

julia> v, b = to_vec(cca); b(v)
T = CustomConstructorArray{Float64, 2, Matrix{Float64}}
ERROR: MethodError: no method matching CustomConstructorArray{Float64, 2, Matrix{Float64}}(::Matrix{Float64})
Stacktrace:
 [1] (::FiniteDifferences.var"#structtype_from_vec#27"{CustomConstructorArray{Float64, 2, Matrix{Float64}}, FiniteDifferences.var"#Tuple_from_vec#43"{Tuple{Int64}, Tuple{Int64}, Tuple{typeof(identity)}}, Tuple{FiniteDifferences.var"#Array_from_vec#32"{Matrix{Float64}, typeof(identity)}}})(v::Vector{Float64})
   @ FiniteDifferences ~/JuliaEnvs/test/alex/dev/FiniteDifferences/src/to_vec.jl:39
 [2] top-level scope
   @ REPL[13]:1

because there is a custom constructor. Not the best solution (one could imagine trying to figure out what the constructor is), but an improvement over the current state.

@oxinabox
Copy link
Member

oxinabox commented Jul 16, 2021

One can bypass inner constructors.
Which might be suitable to do in this case?
is this as valid as assuming a non-parametric constructor exists?

I think it makes sense to bypass constructors because we are working with the fields directly

macro force_construct(T, args...)
          return esc(Expr(:new, T, args...))
end
julia> struct CustomConstructorArray{T, N, A<:AbstractArray{T, N}} <: AbstractArray{T, N}
           data::A
           function CustomConstructorArray(data::A) where {T, N, A<:AbstractArray{T, N}}
               return new{T, N, A}(data)
           end
       end

julia> Base.size(a::CustomConstructorArray) = size(a.data)

julia> Base.getindex(a::CustomConstructorArray, inds...) = getindex(a.data, inds...)

julia> macro force_construct(T, args...)
          return esc(Expr(:new, T, args...))
       end
@force_construct (macro with 1 method)

julia> x = CustomConstructorArray([1.0 2.0; 3.0 4.0])
2×2 CustomConstructorArray{Float64, 2, Matrix{Float64}}:
 1.0  2.0
 3.0  4.0

julia> y = @force_construct(typeof(x), [5.0 6.0; 7.0 8.0])
2×2 CustomConstructorArray{Float64, 2, Matrix{Float64}}:
 5.0  6.0
 7.0  8.0

@mzgubic
Copy link
Member Author

mzgubic commented Jul 16, 2021

Wow, what is this :new magic? I mean I know its role in the inner constructor - but not sure how it works outside that.

In any case, we need to splat the arguments, so the macro would have to be a bit more complicated. I'd like to give it a go if you think it's the right path forward

@oxinabox
Copy link
Member

Wow, what is this :new magic? I mean I know its role in the inner constructor - but not sure how it works outside that.

As I understand it, it is the thing that powers the new insider the inner constructor.
The parser generates that Expr(;new, current_scopes_type, args...) based on the current scope.
And then outside of that context it doesn't (thus preventing evil like ... us).

I think it is the right path.
It does not play nice with integer fields that need to become floating point fields.
But nothing does.
so feels right

@oxinabox
Copy link
Member

oxinabox commented Jul 16, 2021

It seems like 1.3 introduced splatnew

@simeonschaub says for 1.0 we need to do

 @generated force_construct(T, args...) = Expr(:new, :T, Any[:(args[$i]) for i in 1:length(args)]...)

but after 1.3 we can do

@generated v(T, args...) = Expr(:splatnew, :T, :args)

using generated functions rather than macros

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants