Hi everyone.

I was thinking for some time about dimensionful arrays or variables in 
general. (I really *really* *really* don't know whether the idea is sane. 
Actually it tends to create more problem than it solves for what I've 
tried.)
Dimensionful variables would be a variable which refer to data and a 
physical unit. This can easily be implemented either as:

type DimensionfulArray{T<:Number}
    data::AbstractArray{T}
    unit::String
end

There are, however, many problems raised by this approach. For instance:

   1. DimensionfulArray does not inherit from AbstractArray so that every 
   functions should be redefined as Foo(x::DimensionfulArray) = Foo(x.data).
   2. The approach used in point 1. has the huge default to involve a lot 
   of boilerplate/repeated code and a new definition has to be written each 
   time a new function is used on DimensionfulArray.
   3. Most of the time, one does want type invariance: 
    Foo(x::DimensionfulArray) = Foo(x.data) would return a plain array instead 
   of a DimensionfulArray instance (wanted most of the time).
   4. Even if we are consenting adults there, letting the "unit" attribute 
   directly accessible is not a really good practice.
   5. With this approach a lot of "If"s will be put everywhere to check 
   what the "unit" attribute is.

http://grollchristian.wordpress.com/2014/01/22/julia-inheriting-behavior/ 
had the exact same problem and I'm not sure his solution is the most 
maintainable one.
https://groups.google.com/forum/#!topic/julia-users/jlJrMTo_L1M was about 
subtyping concrete types but Stefan redirected to 
https://groups.google.com/forum/#!msg/julia-dev/p9bV6rMSmvY/cExeSb-cMBYJ 
which is about inheritance in general and delegation in particular.

In https://groups.google.com/forum/#!topic/julia-users/Wwn3KHmmm9I, John 
crafted a first implementation of what would become the @delegate macro 
which is now in a RFC process in
https://github.com/JuliaLang/julia/pull/3292.

The 
comment https://github.com/JuliaLang/julia/pull/3292#issuecomment-34450100 
in particular was of a great interest for me as it would allow almost 
painless delegation: the default is to delegate except if methods are 
explicitly declared with DimensionfulArray as an input type. 

I would really like to be able to write:

type DimensionfulAndRankfulArray{T<:Number}
    @delegate data::AbstractArray{T}
    @delegate rank::Int for <, >, ==, !=, <=, >=
    unit::String
end

But as stated 
in https://github.com/JuliaLang/julia/pull/3292#issuecomment-34551204, a 
macro is limited in what it can do. To me, but I have not your skills and 
your experience, there are at least 3 ways to solve the problem:

   - Either let it as it is now. The down side is that any method 
   declaration occurring after the type declaration and acting on 
   AbstractArray won't be taken into account even though the developer wrote 
   that he was delegating by default to the data field. This makes the code 
   harder to grasp, raises strange errors and forbid any code sharing : from 
   the type declaration point, methods will have either to be defined for 
   AbstractArray OR DimensionfulAndRankfulArray, but not both of them at once.
   - Either include delegate in the language and perform delegation at 
   runtime, not at type declaration time which can be heavy and complex.
   - Or  tell the compiler at type declaration time that you are sure that 
   any function accepting type AbstractArray is gonna work with your 
   RankfulArray because you delegated (trait-like approach with delegation :) 
   ):
   

type RankfulArray{T<:Number} <: AbstractArray
    delegate data::AbstractArray{T}
    delegate rank::Int for <, >, ==, !=, <=, >=
end

This could be further simplified as:

type RankfulArray{T<:Number}
    delegate data::AbstractArray{T}
    rank::Int 
end
if there is one and only one delegation in the type definition.

This would look a lot like concrete type inheritance though with all the 
drawbacks that I don't know yet.

As a last word, being able to preserve types in some functions would be 
great, something like:

type in_eV{T<:Number}
    delegate data::AbstractArray{T} keep +, -
end
which would mean that in_eV([1,2,3]);^2 would return Array([1,4,9]) 
but in_eV([1,2,3]) + 2 would return in_eV([3,4,5]).

type in_eV{T<:Number}
    delegate data::AbstractArray{T} keep
end
would keep the type for everything.


I'm really not sure that this does make sense at all.

Reply via email to