TypeFox / typir

Typir is a library for building type systems
MIT License
10 stars 2 forks source link

Initial draft of the type system API #1

Open msujew opened 1 year ago

msujew commented 1 year ago

For anyone who wants to see how this API is supposed to look like. I will accept feedback on the API direction and then start implementing the library.

trusktr commented 1 year ago

Can we see the proposal of what is being implemented somewhere? Or is it all in code only so far?

msujew commented 1 year ago

@trusktr I've created an internal document that shows potential usage of this API before I started working on this. Note that the API has changed a bit while developing this PR, but the main ideas are still there.

```ts // A typed function that initializes a whole generic type system // Every type has a `literal` that can be seen as its "source" // We clarify that each literal is of type `AstNode` const typeSystem = createTypeSystem(); // primitives const stringType = typeSystem.primitive('string'); const numberType = typeSystem.primitive('number'); // Create an `any` type and set is as the top type of the type system const anyType = typeSystem.primitive('any').top(); // operators const plus = typeSystem.operator('+'); // the 'operate' function returns a `Disposable` // In case we allow user-contributed operator overloads, these need to be deleted at some point plus.operate({ operands: stringType, result: stringType }); plus.operate({ operands: [stringType, numberType], order: 'flexible' // 'strict' would mean that number + string would not be valid here result: stringType }); // plus.operate({ operands: [stringType, anyType], result: stringType, priority: -1 }); // classes, structs, interfaces // These structures are classified as "categories" const classType = typeSystem.category('class'); const interfaceType = typeSystem.category('interface'); const structType = typeSystem.category('struct'); classType.assignable(classType, (from, to) => { // This is likely the default implementation for any complex type structure // This will likely be exposed as `defaultNamedAssignability` if (from.literal === to.literal) { return true; } if (from.super.some(e => typeSystem.isAssignable(e, to)) { return true; } return false; }); classType.assignable(interfaceType, defaultNamedAssignability); structType.assignable(structType, defaultNamedAssignability); structType.assignable(interfaceType, defaultNamedAssignability); interfaceType.assignable(interfaceType, defaultNamedAssignability); // In addition, the library also exports a `defaultStructuralAssignability` function // This allows for example for a fully structural type system (such as TypeScript) or a partially structural type system // Such a type system can for example use named assignability for classes, while using structural assignability for interfaces // These types can then be specialized to create their class/struct/interface instances const classInstance = classType.get(classLiteral); // `classInstance` is an disposable instance of the type system. // It features the same API and can be used to implement more complex type behavior // This is an example of a user defined implicit casting function // Like most things, it returns a disposable classInstance.assignable((from, to) => { return typeSystem.isAssignable(implicitCastingType, to); }); // Example of a user defined explicit casting function classInstance.castable((from, to) => { return typeSystem.isAssignable(explicitCastingLiteral, to); }); // Complex type instances can also be generic // We first have to define the type parameters const genericSimpleType = typeSystem.typeParameter({ name: 'T'; }); const genericConstraintType = typeSystem.typeParameter({ name: 'T2', // A constraint if effectively just a function with (Type) => boolean // The type system object provides a few default constraints (such as `extends`) contraints: [ typeSystem.extends(someClassLiteral) ] }); const genericInstance = classType.get({ literal: classLiteral parameters: [genericSimpleType, genericConstraintType] }); // The generic types can be accessed using the `genericInstance` const typeParameters = genericInstance.typeParameters // A generic class can be specialized with type instances const genericInstanceWithArguments = genericInstance.typeArguments([ T1Instance, T2Instance }); // Language validators need to assert that T2Instance fits the constraints typeSystem.assertConstraints(typeParameters[1], T2Instance); // All types can have members // push also returns a disposable // This allows to declare partial classes or extension functions/properties // And also remove them on an update of their declaring document/class classInstance.members.push(...members); // Delegate types const delegateInstance = typeSystem.delegate({ // names are usually optional throughout the whole type system // The type system needs to support inline types name: '...', literal: delegateLiteral, parameters: [], returnType: ..., generics: [] }); // Union types const unionInstance = typeSystem.union({ literal: unionLiteral, generics: [ /** can also support generics */ ] }); ```
JohannesMeierSE commented 9 months ago

Here are my general thoughts about this API proposal:

Some more things we probably need: