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.