You may be stepping twice where once does nicely. `abstract Currency`
establishes Currency as an abstract type [in Julia, by convention, type
names are capitalized; it helps others to stay with that practice]. If we
want to use it `abstract Cash <: Currency` establishes Cash as
sub-abstraction, here as an abstract family of distinct currencies. Then
`type USD <: Cash ... end` and `type GPB <: Cash ... end` give you
distinct currencies, although there is as yet no rate of exchange to allow
conversion from one to the other. If your concern is in replicating "a
generalized" type's body for each kind of Cash, Julia has a couple of very
nice ways which handle that concern.
# lets make symbolic names for some currencies [ :USD is a quick way to
write Symbol("USD") ]
const USD = :USD
const EUR = :EUR
const GBP = :GBP
# and the type (as the fields are immediate, we use immutable Cash{S} <:
Real instead of type .. for speed)
immutable Cash{S} <: Real # supertype `Real` helps subtypes of Currency
(e.g. Cash) work well with other types
a::Int64
b::Int64
function Cash(a,b)
b>=100 && error("Too much of smaller currency unit (over 99).")
(b<0 || a<0) && error("Cannot have negative amounts.")
new{S}(a,b)
end
end
# now to differentiate the currencies
typealias CashUSD Cash{USD}
typealias CashEUR Cash{EUR}
typealias CashGBP Cash{GBP}
# and some summation for a single currency
function Base.(:+){S}(x::Cash{S}, y::Cash{S})
total = 100x.a+100y.a+x.b+y.b
return Cash{S}(div(total, 100), total%100)
end
# to introduce rates of exchange, use convert and a dictionary
fx = Dict( (EUR,USD) => 5/6, (USD,EUR) => 6/5 ) # and the rest
function Base.convert(::Type{CashEUR}, x::CashUSD)
a = x.a * fx[(EUR,USD)]
b = x.b * fx[(EUR,USD)]
# ...
return CashEUR(hi, lo)
end
#
# another way uses an enum (pretend those consts were not entered)
# @enum CURRENCY USD EUR GBP
# Base.show(io::IO, x::CURRENCY) = print(io, string(x)) # makes CURRENCY
enums print more tersely
#
#
of course, now the conversion routines proliferate --- there is a way to
tame them too
(look at how the comparisons (< ..) are defined around lines 300-350
<https://github.com/JuliaLang/julia/blob/master/base/float.jl> <<click that)
There are other ways to approach this, too. I hope that gives you a better
sense of direction.
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
>
>
>