What would be the simplest/best way to encode sign, as in +1 or -1, as a type?
Here’s what I can think of:
A type with an Int field which I then set to either +1 or -1 when setting an instance of said type, and then multiply the value of that field with whatever I want to change the sign of.
A type with a Function field which is a function that multiplies its argument with either +1 or -1.
It may depend on what other fields you have, how important it is to keep space to a minimum.
For example, the BigInt type encodes the sign of the number in the Int32 field that holds the size (i.e. if the size if negative, the number is negative)
I’ll just add that in the vast and rich ecosystem of types that Julia has, it seems like maybe there should be some native Sign type…? This really seems like a fringe case that might not be worth it.
It would be similar to Bool, but function differently…
Just a thought.
Using a Bool like @dpsanders suggests looks like the best solution, and in many respects a Bool already acts like a “sign” type. Specifically:
(-1)^boolvar is -1 / 1 according to boolean and has a specialized ^ method to make it fast.
ifelse(boolvar,expr,-expr) could be used for negating.
It’s true sometimes a specialized type would help, but it can also burden (requiring specialized methods). The -1/1 <==> true/false <==> 0/1 correspondence is too strong to separate these values into different types.
I would consider having a Unit{T} type (or struct) which would represent the units of type T i.e. Sign for Reals, and unit circle for complex and 1/i/-1/-i for integer complex (Gaussian numbers). This type would satisfy abs(v) = v/unit(v) and other such relations which come in useful sometime.
With the amount of new things being added, in base, and in packages, there’s always something new to learn in Julia! (I knew about ifelse, but also didn’t know that ^ had been specialized for ^boolval) ifelse works very well by not causing a branch.
I actually think there is a genuine case for a Sign type that is separate from Bool. The reason for this is that in a package I’m working on, I use Boolean values for two purposes: as strong zeros for multiplication operations, and as signs for indicating negation of a result. Keeping track of the semantics of a Boolean value using types is a good idea from the perspective of code readability: rather than dealing with different Boolean operators to manually implement multiplication, multiplications are always *, defined for different combinations of arguments.
Performance penalties are a good point. For my use case, type stability guarantees and heavy inlining prevent dynamic dispatch from being an issue, but in general I hope that the language implementation has made this less of an issue since this thread was last active.
I implemented a package, SignType.jl (not registered), which provides Sign <: Signed as an abstraction of a sign bit, for those who may be interested.