If you don't need the flexible interrelations provided by J. Sarnoff's 
approach, you might use metaprogramming like this:

abstract Cash


function _validate(p::Cash)
    p.b >= 100 && throw(ArgumentError("Too much..."))
    (p.b < 0 || p.a < 0) && throw(ArgumentError("Cannot have negative 
amounts"))
end


macro make_currency(C)
    esc(quote
          type $C <: Cash
            a::Int64
            b::Int64
            function $C(a::Int64,b::Int64)
              this = new(a,b)
              _validate(this)
              this
            end
          end
          # other boilerplate specific to C goes here
        end)
end


for t = (:Usd,:Gbp,:Whole)
    # show(macroexpand(:(@make_currency $t))) # for debugging
    @eval @make_currency($t)
end

# specialize for the exceptional case:
function _validate(p::Whole)
    p.a < 0 && throw(ArgumentError("Cannot have negative amounts"))
    p.b != 0 && throw(ArgumentError("Must be whole amount"))
end






On Wednesday, August 24, 2016 at 1:35:19 PM UTC-4, Cliff wrote:
>
> As an example of what I mean, suppose that I'm trying to make a currency 
> type for various currencies:
>
> abstract cash
>
> type usd <: cash
>   a::Int64
>   b::Int64
>   function usd(a,b)
>     b>=100 && error("Too much of smaller currency unit (over 99).")
>     (b<0 || a<0) && error("Cannot have negative amounts.")
>     new(a,b)
>   end
> end
> type gbp <: cash
>   a::Int64
>   b::Int64
>   function gbp(a,b)
>     b>=100 && error("Too much of smaller currency unit (over 99).")
>     (b<0 || a<0) && error("Cannot have negative amounts.")
>     new(a,b)
>   end
> end
>
> function Base.(:+){T<:cash}(x::T, y::T)
>   total = 100x.a+100y.a+x.b+y.b
>   return T(div(total, 100), total%100)
> end
>
> I'm able to define addition once for any subtype of cash. On the other 
> hand, I have to write out a large amount of almost identical code each time 
> I want to define a new subtype of cash. Is there some way I can define this 
> once in an abstract manner (with any manually specified constructor 
> overriding the general one), so that we could have something like:
>
> abstract cash
> general type T <: cash
>   a::Int64
>   b::Int64
>   function T(a::Int64,b::Int64)
>     b>=100 && error("Too much of smaller currency unit (over 99).")
>     (b<0 || a<0) && error("Cannot have negative amounts.")
>     new(a,b)
>   end
> end
>
> type usd <: cash end
> type gbp <: cash end
> type whole <: cash
>   function whole(a,b)
>     a<0 && error("Cannot have negative amounts.")
>     b != 0 && error("Must be whole amount.")
>     new(a,b)
>   end
> end
>
>
>

Reply via email to