Separate Type entities and the type information about them #14

Open
opened 2025-04-10 17:41:27 +00:00 by glen · 6 comments
Owner

For each type there will be the explicit conversion method, and the type object itself. These must be different, because generic types are callable as functions: Complex(BigNumber), for example, will produce a high-precision complex number type. This leads to some questions:

(A) I am unclear on whether we will need separate methods for type predicates: we will be able to write math.BigNumber.test(x) to determine if x is a BigNumber, so do we also need something like math.isBigNumber(x) ?

(B) I am pretty sure we want to stick with number, boolean, string, etc. as the explicit conversion methods. So to keep them closely associated, I was going to call the type objects Number, Boolean, String, etc. However, I just had a confusing bug because those names are already used in standard JavaScript (exactly for its explicit conversion operators). So I would like to use different names. What should they be?

For each type there will be the explicit conversion method, and the type object itself. These must be different, because generic types are callable as functions: `Complex(BigNumber)`, for example, will produce a high-precision complex number type. This leads to some questions: (A) I am unclear on whether we will need separate methods for type predicates: we will be able to write `math.BigNumber.test(x)` to determine if `x` is a BigNumber, so do we also need something like `math.isBigNumber(x)` ? (B) I am pretty sure we want to stick with `number`, `boolean`, `string`, etc. as the explicit conversion methods. So to keep them closely associated, I was going to call the type objects `Number`, `Boolean`, `String`, etc. However, I just had a confusing bug because those names are already used in standard JavaScript (exactly for _its_ explicit conversion operators). So I would like to use different names. What should they be?
glen added the
design
label 2025-04-10 17:41:34 +00:00
Author
Owner

For now in the prototype I am going to:

(A) assume the answer is no, we don't need type predicates aside from TheType.test(x)

(B) name type objects corresponding to built-in types (only), where the preferred type object name is already in use, by adding a T suffix to that: NumberT, BooleanT, etc.

However, I would definitely be happy to have feedback on these issues, and it will be no problem to change over to any better system should we come up with one.

For now in the prototype I am going to: (A) assume the answer is no, we don't need type predicates aside from `TheType.test(x)` (B) name type objects corresponding to built-in types (only), where the preferred type object name is already in use, by adding a `T` suffix to that: `NumberT`, `BooleanT`, etc. However, I would definitely be happy to have feedback on these issues, and it will be no problem to change over to any better system should we come up with one.
Collaborator

(A) I have no strong opinion in this regard. I prefer to have 1 way to do things though.
(B) Is it needed to create type objects on top of the primitive types (which are already built in)? If so, it will be good indeed to give them a different name. Maybe NumberType? The abbriviation NumberT looks a bit "magic".

(A) I have no strong opinion in this regard. I prefer to have 1 way to do things though. (B) Is it needed to create type objects on top of the primitive types (which are already built in)? If so, it will be good indeed to give them a different name. Maybe `NumberType`? The abbriviation `NumberT` looks a bit "magic".
Author
Owner

(A) OK, I will leave this aspect as is: to explicitly check if x is of type Foo, call Foo.test(x). It has barely come up so far, so it seems OK.

(B) Well, we record a lot of information about each type: what automatic conversions it supports; its zero, one, and/or nan elements if it has them; whether it is generic or concrete; how to specialize it if it is generic, etc. It seems convenient to keep all of that information on the type entities themselves, but if you prefer, we could keep a Map from the type entities to plain objects with all of that information. Then the test of whether something is a type entity would simply be whether it is a key in that Map, and so we could explicitly use the built-in entities Number and Boolean as the type entities for the number and boolean types.

The only slight complication in this alternate plan for (B) is when merging entities into a TypeDispatcher, we would have to institute some new explicit way of flagging that the item being registered is a type: right now since type entities are just instances of class Type, it just checks if the item is such an instance, and if so, registers it as a type. If the test for typehood becomes "has this entity been registered as a type?" then we must directly inform the TypeDispatcher that an entity being registered should be registered as a type rather than a method or property.

Let me know as soon as you have a chance whether you prefer the alternate plan for (B), since if so, I'd like to switch over as the next step. If you prefer to stay with a Type class as things are now, then we face the naming issue. I'd like to keep the names as short as possible, since they are used a lot in the code. So can you think of anything shorter than NumberType that's OK with you? TNumber? Num? (and in the latter case, then Bool instead of BooleanT?)

(A) OK, I will leave this aspect as is: to explicitly check if x is of type Foo, call Foo.test(x). It has barely come up so far, so it seems OK. (B) Well, we record a lot of information about each type: what automatic conversions it supports; its zero, one, and/or nan elements if it has them; whether it is generic or concrete; how to specialize it if it is generic, etc. It seems convenient to keep all of that information on the type entities themselves, but if you prefer, we could keep a Map from the type entities to plain objects with all of that information. Then the test of whether something is a type entity would simply be whether it is a key in that Map, and so we could explicitly use the built-in entities `Number` and `Boolean` as the type entities for the number and boolean types. The only slight complication in this alternate plan for (B) is when merging entities into a TypeDispatcher, we would have to institute some new explicit way of flagging that the item being registered is a type: right now since type entities are just instances of class Type, it just checks if the item is such an instance, and if so, registers it as a type. If the test for typehood becomes "has this entity been registered as a type?" then we must directly inform the TypeDispatcher that an entity being registered should be registered as a type rather than a method or property. Let me know as soon as you have a chance whether you prefer the alternate plan for (B), since if so, I'd like to switch over as the next step. If you prefer to stay with a Type class as things are now, then we face the naming issue. I'd like to keep the names as short as possible, since they are used **a lot** in the code. So can you think of anything shorter than `NumberType` that's OK with you? `TNumber`? `Num`? (and in the latter case, then `Bool` instead of `BooleanT`?)
Collaborator

(A) 👍
(B) I have a strong feeling that it would be better to keep the data type itself and the type information about the data type separate from each other, but I don't have a full picture of the consequences of both ways right now. When keeping the "meta information" separate, we keep the data types clean and standalone, allowing to use both built-in types like Number and BigInt in signatures, but also use 3rd partly libraries in a natural way, libraries like for BigNumber and Fraction. I think it is quite doable but indeed in the type configuration like NumberType needs a way to configure the relation to Number because that will be used in type signatures. What do you think?

(A) 👍 (B) I have a strong feeling that it would be better to keep the data type itself and the type information about the data type separate from each other, but I don't have a full picture of the consequences of both ways right now. When keeping the "meta information" separate, we keep the data types clean and standalone, allowing to use both built-in types like `Number` and `BigInt` in signatures, but also use 3rd partly libraries in a natural way, libraries like for `BigNumber` and `Fraction`. I think it is quite doable but indeed in the type configuration like `NumberType` needs a way to configure the relation to `Number` because that will be used in type signatures. What do you think?
glen changed title from Naming of type objects to Separate Type entities and the type information about them 2025-05-15 13:50:02 +00:00
Author
Owner

OK, fine, I will switch to a scheme along the lines of

export numberTypeDefinition = new TypeDefinition('Number', {
   test: x => typeof x === 'number',
   convert: blah
   blah blah blah
})

and just put in the TypeDefinition constructor some special property setting that the TypeDispatcher merge function can notice and therefore treat this item specially, registering a new type.

OK, fine, I will switch to a scheme along the lines of ``` export numberTypeDefinition = new TypeDefinition('Number', { test: x => typeof x === 'number', convert: blah blah blah blah }) ``` and just put in the TypeDefinition constructor some special property setting that the TypeDispatcher `merge` function can notice and therefore treat this item specially, registering a new type.
glen added the
priority
maintenance
labels 2025-05-15 13:54:21 +00:00
Collaborator

👍

👍
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: StudioInfinity/nanomath#14
No description provided.