This sounds a bit like a mix of two problems: (1) A lack of interfaces:
- a) A lack of formal interfaces, which will hopefully be addressed by something like Traits.jl at some point. (https://github.com/JuliaLang/julia/issues/6975) - b) A lack of documentation for informal interfaces, such as the methods that AbstractArray objects must implement. (2) A lack of delegation when you make wrapper types: https://github.com/JuliaLang/julia/pull/3292 The first has moved forward a bunch thanks to Mauro's work. The second has not gotten much further, although Kevin Squire wrote a different delegate macro that's noticeably better than the draft I wrote. -- John On Nov 21, 2014, at 2:31 PM, Sebastian Good <sebast...@palladiumconsulting.com> wrote: > In implementing new kinds of numbers, I've found it difficult to know just > how many functions I need to implement for the general library to "just work" > on them. Take as an example a byte-swapped, e.g. big-endian, integer. This is > handy when doing memory-mapped I/O on a file with data written in network > order. It would be nice to just implement, say, Int32BigEndian and have it > act like a real number. (Then I could just reinterpret a mmaped array and > work directly off it) In general, we'd convert to Int32 at the earliest > opportunity we had. For instance the following macro introduces a new type > which claims to be derived from $base_type, and implements conversions and > promotion rules to get it into a native form ($n_type) whenever it's used. > > macro encoded_bitstype(name, base_type, bits_type, n_type, to_n, from_n) > quote > immutable $name <: $base_type > bits::$bits_type > end > > Base.bits(x::$name) = bits(x.bits) > Base.bswap(x::$name) = $name(bswap(x.bits)) > > Base.convert(::Type{$n_type}, x::$name) = $to_n(x.bits) > Base.convert(::Type{$name}, x::$n_type) = $name($from_n(x)) > Base.promote_rule(::Type{$name}, ::Type{$n_type}) = $n_type > Base.promote_rule(::Type{$name}, ::Type{$base_type}) = $n_type > end > end > > I can use it like this > > @encoded_bitstype(Int32BigEndian, Signed, Int32, Int32, bswap, bswap) > > But unfortunately, it doesn't work out of the box because the conversions > need to be explicit. I noticed that many of the math functions promote their > arguments to a common type, but the following trick doesn't work, presumably > because the promotion algorithm doesn't ask to promote types that are already > identical. > > Base.promote_rule(::Type{$name}, ::Type{$name}) = $n_type > > It seems like there are a couple of issues this raises, and I know I've seen > similar questions on this list as people implement new kinds of things, e.g. > exotic matrices. > > 1. One possibility would be to allow an implicit promotion, perhaps expressed > as the self-promotion above. I say I'm a Int32BigEndian, or CompressedVector, > or what have you, and provide a way to turn me into an Int32 or Vector > implicitly to take advantage of all the functions already written on those > types. I'm not sure this is a great option for the language since it's been > explicitly avoided elsewhere. but I'm curious if there have been any > discussions in this direction > > 2. If instead I want to say "this new type acts like an Integer", there's no > canonical place for me to find out what all the functions are I need to > implement. Ultimately, these are like Haskell's typeclasses, Ord, Eq, etc. By > trial and error, we can determine many of them and implement them this way > > macro as_number(name, n_type) > quote > global +(x::$name, y::$name) = +(convert($n_type, x), > convert($n_type, y)) > global *(x::$name, y::$name) = *(convert($n_type, x), > convert($n_type, y)) > global -(x::$name, y::$name) = -(convert($n_type, x), > convert($n_type, y)) > global -(x::$name) = -convert($n_type, x) > global /(x::$name, y::$name) = /(convert($n_type, x), > convert($n_type, y)) > global ^(x::$name, y::$name) = ^(convert($n_type, x), > convert($n_type, y)) > global ==(x::$name, y::$name) = (==)(convert($n_type, x), > convert($n_type, y)) > global < (x::$name, y::$name) = (< )(convert($n_type, x), > convert($n_type, y)) > Base.flipsign(x::$name, y::$name) = Base.flipsign(convert($n_type, > x), convert($n_type, y)) > end > end > > But I don't know if I've found them all, and my guesses may well change as > implementation details inside the base library change. Gradual typing is > great, but with such a powerful base library already in place, it would be > good to have a facility to know which functions are associated with which > named behaviors. > > Since we already have abstract classes in place, e.g. Signed, Number, etc., > it would be natural to extract a list of functions which operate on them, or, > even better, allow the type declarer to specify which functions *should* > operate on that abstract class, typeclass or interface style? > > Are there any recommendations in place, or updates to the language planned, > to address these sorts of topics? > > > > >