Rational numbers are really nice for some things. If you’re using rational numbers for time, for instance, you can represent times from ½^63 seconds to 2^63 seconds exactly. You can represent a third of a second. Or a fifth.
Unfortunately, you have two dimensions of overflow, not just one.
No. A character type is misleading. It makes you think that ‘é’ should be an 8-bit value. Or if the only character type is 32-bit, it makes you think that ‘é’ should be one character, just like ‘é’. It makes you think you should be able to get the character at position n in constant time, and that’s just not possible.
string
instead has explicit functions for each, but tends to work on a code unit basis:
string.bytes
is a uint8[]
slice of the same memory.string[start -> end]
is a shorthand for string(string.bytes[start -> end])
.string.byCluster
iterates through character clusters with type string
. This is the default
iteration method.string.byCodepoint
iterates through codepoints with type uint32
.string.byCodeUnit
iterates through code units with type uint8
.No. The runtime will pretend that the entire world is on UTF-8. The standard library will include an encoding section. This will support other encodings.
No. We want to share code (such as Unicode data) between the standard library and the runtime.
No. Unions are an advanced, unsafe feature. They let you do some useful
things, but on the whole, we want to leave unions for a compiler-provided any
type.
No. We will use a library-provided type. Something like:
struct Maybe(T)
{
null { value = Unit(). }
set T v { value = v. }
get -> T { assert value !is Unit. return value as T. }
private: let any value = Unit().
}
I suspect that, with a little practice, the need to use Maybe
will be minimal in many
applications.
We will have aliases. What about a typedef? A typedef is an exact duplicate of a type. It’s a type that differs from its input type only in name. They’re typically mutually convertible, but only with an explicit cast.
These are only rarely useful. D’s std.typecons.Typedef handles this by making the typedef’d type
entirely opaque; a Typedef!int
can’t be incremented, a Typedef!MyType
has none of the members of
MyType
exposed; etc. This sucks.
A more thorough treatment would be to treat it as if you’d copy/pasted the source code and swapped the names around.
class Foo base Stream
{
int i.
this this:i.
next -> Foo { return Foo(i + 1). }
same Foo f -> bool { return this is f. }
}
typedef Bar = Foo.
# expands to:
class Bar base Stream
{
int i.
this this:i.
next -> Bar { return Bar(i + 1). }
same Bar f -> bool { return this is f. }
}
With sufficient metaprogramming, we could make this happen:
# Alternate expansion
struct Bar
{
private let Foo _self.
private this this:self.
this int i { _self = Foo(i). }
next -> Bar { return Bar(_self:next). }
same Bar f -> bool { return _self:same(f:_self). }
}
I’m not that sold on typedefs, so we’re going with “no” for now.
Yes.
Let’s say we want some objects to opt into advanced formatting. Like I have a Currency
type that
should be formattable with similar options to floating point numbers. It needs to inherit from
Formattable
. But that shouldn’t affect its base class options. Or Serializable
likewise.
And it’s not hard to imagine something that’s both formattable and serializable.