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?
> 
> 
> 
> 
> 

Reply via email to