Forward all methods of a structure

For example we have a struct

struct Daggered
    block::Matrix
end

For any function trait(m::Matrix, args...), can we define

trait(m::Daggered, args...) = trait(m.block, args...)

automatically to make Daggered look like a real matrix?

1 Like

You code do this with some code generation. As a simple example

for op āˆˆ ops_i_want_to_use
    @eval $op(m::Daggered, args...) = $op(m.block, args...)
end 

though ideally you shouldnā€™t use splatting. In practice I often see people do this separately for unary, binary and trinary operators, but another (probably better) alternative would be to use MacroTools.jl which provides some really useful tools for deconstructing function syntax.

Note also that ā€œDaggeredā€ already exists, itā€™s called Adjoint and you can get it by just doing A' or adjoint(A) for some matrix A. (Be sure to import the package Compat first, as Iā€™m showing syntax for 0.7 and I think adjoint was called something else in 0.6.)

One further comment, Iā€™m not sure if your example was just demonstrative or intended as an actual use, but in general, if you want to create an array type, it should inherit from AbstractArray. For example

struct Daggered{T,N} <: AbstractArray{T,N}
    block::Array{T,N}
end

this will then inherit all of the methods of AbstractArray (which is a lot). You only need to implement a few simple functions to make it work. One of the great virtues of Julia is that inheriting from abstract types in Base is a much more common practice than in any other language I have seen, and arrays are a really good example of how powerful this can be.

1 Like

There is also the @forward macro from Lazy.jl:

help?> Lazy.@forward
  @forward T.x functions...

  Define methods for functions on type T, which call the relevant function on the field x.

     Example
    ā‰”ā‰”ā‰”ā‰”ā‰”ā‰”ā‰”ā‰”ā‰”

  struct Wrapper
      x
  end
  
  @forward Wrapper.x Base.sqrt             # now sqrt(Wrapper(4.0)) == 2.0
5 Likes

Thanks for your reply,

code generation is a good idea, but not good enough.
For future extensions, we still have to define two set of functions for both Daggered and original version.

Adjoint has the same problem.

Iā€™m not quite sure Iā€™m understanding you correctly, but it sounds like you want to define a new function for arrays, and then also extend that to a new type.

It might help if you could give a broader explanation of what youā€™re trying to do. Itā€™s still sounding a bit like you just want some new AbstractArray methods, in which case what you should do is define Daggered <: AbstractArray and then simply define some methods which take an AbstractArray argument, i.e.

trait(A::AbstractArray, args...) = # some code

(perhaps see some documentation on abstract types).

Again, not sure if this is a real example, but Daggered of a matrix sounds quite a lot like an adjoint to me, so it might be worth looking through some of the array and linear algebra docs to see if what youā€™re doing is already implemented.

Daggered may be a poor example, it was defined under the context of quantum circuit simulation in my case. Never mind ā€¦

I have read the source code of Adjoint, I think it is not elegant.

Letā€™s use the Cached object as an example,
In real programming, we often need something like Cached object that sharing the same interface with original object. It has difference only in some specific methods related to caching.

I need this kind of logic ā€œstruct A is same as B, only for functions in a countable set, it is treated special.ā€

Thatā€™s sounds quite a lot like inheritance to me. Ideally youā€™re objects would inherit from a common abstract type. If you are working with existing types that canā€™t do that, you can still generate functions as discussed above, but you may need to look through Julia introspection tools for an automated way of finding them all.

For something like quantum circuits, I donā€™t think itā€™s a good idea to completely forsake all of the nice linear algebra functionality already existing in Base, so itā€™s good to at least be aware of it. Indeed, even if you have specialized operator types, itā€™s probably a good idea to also have methods which use AbstractArrays since in many cases the specialized operators will simply wrap those. At least in the finite dimensional case of quantum circuits, I canā€™t think of any reason why operators shouldnā€™t be AbstractArrays, at least off the top of my head. It might be worth taking a look at QuantumOptics for some inspiration, itā€™s quite a nice package.