One thing I miss when programming in Wren is not being able to perform (full) 64 bit integer arithmetic, as we are effectively limited to no more than 53 bits because of the way that Nums
are represented.
I've been wondering whether there might be a way to achieve this without impacting on the simplicity of dealing with smaller integers and numbers generally that the Num
class gives us and have come up with an idea.
This is a fairly rough sketch so please bear with me.
The basic idea is to introduce a Long
class into the core library. A Long
object would be 8 bytes long and would be represented internally by a C99 signed long long
. It would be immutable as numbers are now.
Consequently, a Long
would be a value type as a variable of that type would hold its value directly.
The Long
class would not have constructors but instead a Long
object could be created in one of three ways:
-
From a literal - an integer with an 'L' suffix. Lower case 'l' would not be allowed as it's too easily confused with the digit '1'.
-
From a string by giving the String
class a toLong
method which could optionally specify a base from 2 to 36.
-
From a number by giving the Num
class a toLong
method.
The Long
class itself would support all the usual integer operators:
+ - * / % & | ^ ~ << >> < <= == != >= and >.
However, the operands would always need to be Long
objects - mixed operations with Nums
would not be allowed.
We'd also need a few methods such as: largest
, smallest
, min
, max
, pow
and sign
as well as toNum
and toString
.
Here's a few simple examples to show how some of this would look:
var r = 123456789L // Long created from a literal
var s = "123456789123456789".toLong // Long created from a String
var t = 12345678912345.toLong // Long created from a Num
var u = "abcdef".toLong(16) // Long created from a base 16 string
var v = s + t * u // arithmetic operations on Longs
var w = (s > t) && (t > u) // relational operations on Long
var x = s + 6 // not allowed, can't add Num to a Long
var y = s + 6L // fine
var z = r.toNum // Long converted to Num
Internally all operations would be performed from the C side using long long
arithmetic and conversion functions such as strtoll
.
There would clearly be a lot of work needed to implement this and so the question is would it be worth it?
On the plus side:
-
We'd get full 64-bit integer representation and arithmetic including bit-wise operations though the latter might necessitate an internal conversion to unsigned long long
. It would probably be best for overflows to wrap rather than producing an error.
-
As we'd just be dealing with integers, base conversions would be straightforward to implement.
-
Integer division would be just that (i.e. any remainder would be discarded) which is something I was banging on about in #907.
-
There would be no impact whatsoever on Num
arithmetic.
-
It would be backwards compatible.
On the minus side:
-
There would be a certain amount of inconvenience in not permitting mixed arithmetic with Nums
.
-
There would inevitably be an impact on the size of the core library though I don't really see how it could done as an optional module as you'd need support for Long
literals in the language itself.
-
Using a numeric suffix for literals is arguably a bit ugly but I can't think of a sensible alternative and, of course, C has used suffixes from day one.
-
A Long
would have a lower range than an unsigned 64 bit integer but I feel that the convenience of having signed integers outweighs this.
-
There would be more for newcomers to the language to learn.
Finally, I'd add that you could do a lot of this with a custom type which is represented internally by a List
of two Nums
. However, the trouble with this is that it's relatively slow and, as it's a reference type, it will be subject to garbage collection and can't be used directly as a Map
key.
I look forward to hearing what you all think.