So the PERCENT! datatype didn't exist in Rebol2:
rebol2>> 10%
** Syntax Error: Invalid integer -- 10%
But it was added to R3-Alpha, and then included in Red. Both of which seem to believe that percents are actually just a rendering of an equivalent decimal number, divided by 100:
r3-alpha/red>> to percent! 10
== 1000%
r3-alpha/red>> to percent! 10.0
== 1000%
r3-alpha>> to decimal! 10%
== 0.1
red>> to float! 10% ; Red calls decimal "float", but same difference
== 0.1
This shows a kind of mathematical invariant in R3-Alpha, e.g. that 10% will act like decimal 0.1 in math... so that's what it "is":
r3-alpha>> 2 * 10%
== 0.2
r3-alpha>> 10% * 2
== 0.2
Red of course throws in a curve ball... it will either double the percent or apply it to the non-percent, depending on the order...
red>> 10% * 2
== 20%
red>> 2 * 10%
== 0.2
How about some other math...?
r3-alpha>> 10% * 10%
== 1.0000000000000002%
red>> 10% * 10%
== 1%
r3-alpha>> 10% + 2
== 2.1 ; same for 2 + 10%
red>> 10% + 2
== 210%
red>> 2 + 10%
== 2.1
Thoughts On That TO Conversion
One thing is that if we follow my "useless" reversible model of TO conversions to percent! 10
should fairly clearly be 10%.
>> to percent! 10
== 10%
>> to integer! 10%
== 10
>> to text! 10%
== "10"
>> to percent! "10"
== 10%
>> to block! 10%
== [10]
I have explained why I have been promoting this perhaps-strange idea. You get "if ten were a block, what would it look like"... "if ten were a percent, what would it look like"... with an emphasis on "look like".
Consider the challenge question: how would you turn a variable containing the integer 10 into 10% in R3-Alpha or Red today?
red>> num: 10
red>> what-operation-here num
== 10%
Your answers are things like:
red>> to percent! num / 100
== 10%
red>> load rejoin [num "%"]
== 10%
red>> num * 1%
== 10%
But I think there should be an operation that just does that. In the system I've described, TO seems to fill this need, in a way that you can predict. Of course, that means TO is not the end-all be-all operation, and I've discussed how breaking operations out into things like JOIN and ROUND and FORM can step in for other intents.
One operation that might be an example of something that could fill in a gap here could be AS
. AS allows us to ask "what if the memory or guts of the thing we're looking at were viewed through a different lens"?
>> as binary! "ABC"
== #{414243}
>> as text! #{414243}
== "ABC"
>> as decimal! 10%
== 0.1
>> as percent! 0.1
== 10%
It's one possibility of an operator that wouldn't be constrained by the equivalence classes of TO.
What About That Math?
Red's policy of making the result match the first operand feels confusing. It seems easier if there's some pecking order of type promotions, and you get the same result regardless of order. (Of course, things like matrix multiplication throw a wrench in the idea that you can say multiplication is always commutative... so, exceptions exist. Unless you do like Python and make matrix multiplication a separate operator, and reserve *
for element-wise multiplication.)
Regarding picking behaviors: percent is a pretty strange situation, because there are of course different questions:
- What happens when you double 20% ?
- What is 20% of 2?
My default mindset would be to imagine that multiplying 20% times 2 is meant to double it, and produce 40% (which Red half agrees with). If I expected to actually take 20% of 2, I wouldn't think it unreasonable to be asked to first convert the percent to a decimal...
>> 20% * 2
== 40%
>> 2 * 20%
== 40%
>> (as decimal! 20%) * 2
== 0.4
>> 2 * (as decimal 20%)
== 0.4
Furthermore...from a perspective of dimensional analysis, I would say you shouldn't be able to do things like:
>> 20% + 1
** Error: Can't add PERCENT! and INTEGER!
But if you could do it, I would probably lean to it being more generally useful to give back 21% than 120%...which seems to be a behavior straight out of the annals of improbable usefulness.
I don't really use the percent type, but it seems to me there's simply too much guessing what people want out of it. I imagine that it best serves its purpose as just kind of a source-level lexical convenience, and your code has to decide when it wants to convert it to a decimal for the purposes of some kind of measurement.
Red's first answer here makes sense to me, the second does not--I don't see the order as distinguishing "give me half of 10 percent" and the second "give me 10% of 0.5":
red>> 10% * 0.5
== 5%
red>> 0.5 * 10%
== 0.05
So my plan is to pretty much pare down the percent type to bare bones, try to make the math commutative, and favor the idea that you have to explicitly convert a percentage to a decimal to get it to behave like a decimal... probably via the AS operator, doing the TO operator as described.