@enum multiplication

Is this the best way to do this?

@enum BuySell (Buy=1; Sell=-1)

import Base.*
*(x::BuySell,  y::Any    )  = Int(x)   * y
*(y::Any,      x::BuySell)  = y        * Int(x)

Sell * 10    #  = -10

So I can put * Buy or * Sell in any equation.

Not trying to be facetious here, but why not

julia> Sell = -1
-1

julia> Sell * 10
-10

(I’ve never really understood what enums are for, so I’m probably missing something here!)

I’m setting up deal structures like this

@enum BuySell Buy Sell
@enum CCY AUD USD GBP EUR CAD NZD CHF

struct SomeDeal
    Currency::CCY
    Buy_Sell::BuySell
    Notional::Float64
end

I figured using enums for field types (instead of strings or symbols) would give

  • less memory
  • faster lookups
  • clarity (knowing exactly what values a field can take)

If this is wrong or there’s a better way to do it please let me know.

In a relational database, BuySell and CCY would be primary keys for their respective tables.
The BuySell and CCY fields within the Deals table would have their values restricted via PK-FK relationships to these other tables.

In Julia I’m working with DataFrames and Arrays of defined structures (e.g. SomeDeal).
I’d like to have similar PK-FK relationships between structures.

enums is the best I can think of

This enum package supports arithmetic

CEnum.jl

For adding arithmetic yourself, I think your approach is probably correct (or mostly). It is type piracy. But, if you are not making a package to release for general use, that’s ok.

I wrote a package (unregistered) MEnums.jl that allows you to mutate, that is add more items, to an existing enum. It is a new type. I did not support arithmetic. I don’t know how often arithmetic is needed. I am starting to think adding it to MEnums.jl is a good idea. You might also like the extensibility for your application.

I wrote MEnums.jl with the idea of using them in a situation where you are tempted to make a lot of types and iterate over a heterogeneous array, dispatching on them. This is not efficient. But, I haven’t yet explored this further. My motivations were somewhat related to yours.

For the most part, I copied code from the Enum in Base.

1 Like

CEnum.jl seems to work for +, - but not *

If it worked for * it would be the best solution. Thank you

My mistake. I saw that they import * from Base, which is why I thought it was supported. But, in fact, although it’s imported, they never use it.

can’t hurt to ask :slight_smile:

1 Like

also, do you know of any package that automatically maps a string or symbol to the corresponding enum?

e.g. "Buy" or :Buy to enum Buy.

I’ve been using instances() to create dictionaries to map Symbols to enums.
It would be nice to have a function to do it automatically.

julia> @enum Color red blue green;

julia> csym = :red;

julia> eval(csym)
red::Color = 0

But this is slow: 45ns on my machine. I’m guessing that the fact that you want to do multiplication with the enum instances and also access them via strings or symbols means that there is a better design to get what you want. Maybe without enums.

This works

struct Buy end 
struct Sell end
BuySell = Union{Buy,Sell}

struct deal
    N::Int64
    Buy_Sell::BuySell
end

Buy_Sell(::Buy, x) =  x
Buy_Sell(::Sell,x) = -x

Position_Value(d::deal) = Buy_Sell( d.Buy_Sell, d.N )

d = deal(10,Sell())

julia> Position_Value(d)
-10

But I’d really like to do something like this.

Buy_Sell = Union{Buy => +, Sell => -}
deal(10,Sell)
Position_Value(d::deal) = d.Buy_Sell    d.N

I don’t know of any other way to restrict the possible values a field can take. @enum restricts to labelled numbers, Union restricts to a set of types. Is there any way (as above) to restrict to labelled operators ?

I suppose enums in Base Julia, and all the packages take 1 byte (and in C always?). Primitive types in Julia are a minimum 8 bits (and then multiples of 8), e.g. primitive type Bool <: Integer 8 end.

It was decided to not bring bitfields over from C/C++, to not have the language too complex, but the above limitation could be relaxed someday, to allow 1-bit Bools. Bitfields ARE provided here (even “fully supports insane C”), for C interop, but I’m not sure if this package is useful for just using in Julia without C interop:

I’m just showing more compressed is possible, as with:

I suppose it would be great if your example, as is (or with different Notational), could simply be more compressed:

struct SomeDeal
    Currency::CCY  # could in theory take 1 bit, and combine with the next field, thus those too taking 1 byte:
    Buy_Sell::BuySell # actually in your case only needs 3 bits, 7 should be plenty, are there more than 128 currencies (in total, not just popular), not counting ALL crypto...? They are in the thousands...
    Notional::Dec32  # Maybe Float16, or Dec64?
end

You could encode that struct in 5 bytes not 6 (or your original 10), with a hack in 3 (less precision, different type), or a maybe 4.

Julia does consider each field in the struct separately, and thus must expand each to the minimum 8 bits, but applying a macro to the whole struct could change that assumption without changing the core language (also if primitive changed in the language).

PostgreSQL has Float32 (there “real”; only 6 decimal digits precision) and Float64, no Float16, and the “decimal” type which is variable length, I suppose 3 or 4 bytes for the length alone (as for text strings), but at least in RAM in Julia you could get to 2 bytes with Float16.

This only supports Dec32 as smallest (same as the standard), supporting “7 decimal digits”, but you do not seem to care about good (decimal) precision, maybe Float16 would work for you, and if you premultiply by 1000 then I think you 3 correct decimal digits.

Alternatively if you choose to go with Float32 you could steal lowest 3+ bits you need from the Significand (and maybe even steal the sign bit…). I would have to look into it, but stealing bits from Dec32 might be dangerous (because of its different (refined)
Chen–Ho encoding).

1 Like

Many interesting points. Thank you :slight_smile: