Representing Type Lattices Compactly
bernsteinbear.com
bernsteinbear.com
It's better to think of these types as different. Maybe "primitive types" or "shapes".
You can still have the same information (depending on the actual system), just represented differently - parametric polymorphism is a bit too rigid here because not everything fits neatly into generic schemas.
(Admittedly I'm in the same boat, I don't know the math but stumbled upon implementing my own type system recently)
int8 <= int16 <= int32 <= int64 <= bigint
For this mini subtype hierarchy, you can literally just number the types in increasing order, and use `<=`, rather than bitmasks.For inclusion of unsigned integers, the same rule applies, but each `unsigned _BitInt(N)` is also a subtype of `signed _BitInt(N+1)`. It might make sense to number the unsigned integers one higher than their signed variants of the same width to simplify testing for compatibility. A specific bit, such as 1000b could indicate a signed integer.
UNSIGNED = 0000b;
SIGNED = 1000b;
enum IntType {
UINT8 = UNSIGNED | 1,
UINT16 = UNSIGNED | 2,
UINT32 = UNSIGNED | 3,
UINT64 = UNSIGNED | 4,
UBIGINT = UNSIGNED | 7,
INT8 = SIGNED | 0,
INT16 = SIGNED | 1,
INT32 = SIGNED | 2,
INT64 = SIGNED | 3,
BIGINT = SIGNED | 7,
};
is_uint8_subtype(ty) = (ty | UNSIGNED) <= UINT8
is_uint16_subtype(ty) = (ty | UNSIGNED) <= UINT16
is_uint32_subtype(ty) = (ty | UNSIGNED) <= UINT32
is_uint64_subtype(ty) = (ty | UNSIGNED) <= UINT64
is_natural(ty) = (ty | UNSIGNED) <= UBIGINT
is_int8_subtype(ty) = (ty | SIGNED) <= INT8
is_int16_subtype(ty) = (ty | SIGNED) <= INT16
is_int32_subtype(ty) = (ty | SIGNED) <= INT32
is_int64_subtype(ty) = (ty | SIGNED) <= INT64
is_integer(ty) = (ty | SIGNED) <= BIGINT
This is approximately how I handle it the numerical tower in my interpreter (extended with some ad-hoc rules to also support rationals, real, complex and quaternions).I wonder if a type system exists that can modify the type lattice at run-time or just before run-time, so user-defined types can be added to a system even if the program is already built. As the types usually require explicit language support but I can always foresee more explicit un-typable things that might be useful just not useful enough to put in a general type system
You could probably represent a lot more complex relations with similar strategies by adding one or two cleanup instructions to union/intersection operations, but whenever I've tried to do it, my head gets dizzy from all the possibilities. And so far I've been unable to find software that can assist in generating such functions.
Obviously not perfect as it can produce false positives, but if we keep a filter of sufficient size, this will be low, and still be more space-efficient than keeping a bit per type in a large type hierarchy.
Intersections can also be done, but with a potentially higher false-positive rate. The result of `Bloom(t1, t2)` has at least the bits set by `Bloom(t1) & Bloom(t2)`.
It all reads so technical and long and mathematically academic but it’s just a bit mask.
I absolutely hate writing python now because I have to reason about a “list of list of errors” type defined by a teenager and they get mad at me if I don’t then define a new “list of list of errors, or none” type when I manipulate it. You guys are now employed by VSCode to make those hints look beautiful. VSCode is your boss now and your job is to make VSCode happy
I predict that in five years we will retvrn to duck typing in the same way that we are now retvrning to server side rendering and running on bare metal. Looking forward to the viral “you don’t need compound types” post on here. “Amazing - you can write code which does normal business tasks without learning or ever thinking about homotopy type theory”
Yes I get it if we are writing embedded code or navigation systems or graphics or whatever, please help yourself from the types bucket. Go ahead and define a dictionary of lists where one list can contain strings but all the other lists either contain 8-bit integers or None. But the academic cachet of insanely complex composable type systems bleeds through into a web server that renders a little more than “hello world” and it ruins my life
I'm also unsure how you would represent a "list of list of errors" succintly without calling it just that. A list of a list of errors. An `Error[][]`. How would "duck typing" as you describe make this type somehow less complex?
lmaooo sub-2σ golem detected