Concept for Kipper Type System #562
Luna-Klatzer
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Preface
This post aims to roughly show the behaviour planned for the Kipper type system present in the high-level language, as well as the output types in the output code. Although this is not the finished specification and changes WILL occur.
Intro to types
The type system for Kipper first and foremost attempts to implement the following types and the possibility to work with them:
any
undef
null
void
num
str
bool
obj
Array<T>
[Generic]Func<P..., R>
[Generic]interface I { ... }
(Interfaces) [User Defined]class C { ... }
(Classes) [User Defined]{ ... }
(Anon-Object) [Compiler Infered]All the primitive types are rather easily explained and are already present in Kipper as of
v0.10.4
, so this section will only focus on the more complex types and their role in the language.Array<T>
This type will be one of the two built-in generics and will accept any value for
T
(for now only single types are allowed but unions may be also implemented`). Defining an array will work exactly the same as it does in the underlying languages and can be done in the following manner:As an array is a type with children, all children types must also then be checked against the provided generic argument
T
. This also means mixing different data types will not work.For example:
Like with any other languages, indexing using the
[V]
bracket notation is valid as well as slices `[S:E] , which are already implemented for strings in Kipper.Func<P..., R>
This is a type which basically allows storing a function in a variable and specifying the required type for it, so when it is called required type checks can also be made. This type works with duck-typing and as such restricting the type is always possible (so
Func<num, void>
can be assigned toFunc<void>
), just not extending it.For example:
Interfaces
Interfaces are user-specified types which allow the specification of a blueprint which can be checked against using duck-typing (i.e.
If it walks like a duck and it quacks like a duck, then it must be a duck
). Interfaces in this regard will be exactly like the ones in TypeScript, although with the exception that type assertations will be possible beyond just forcing a cast to an interface type at compile time.So for example:
If we were then to cast it from a type which does not implement all the interface members, the compiler will not be able to determine the validity at compile time and generate a run-time check. The user though will then have to either use
try as
orforce as
.So for example:
When trying to resolve this using
try as
the object would then beMoreProperties?
i.e. nullable:Using
force as
on the other hand would then returnMoreProperties
but an invalid cast would then throw at runtime a type error:Classes
Classes in Kipper will be exactly the same as in the underlying TypeScript system and will follow a strict prototype system, where duck-typing is not used. This does not mean though that an instance of the class can't be assigned to an interface type, just that casting an object to the class type with the exact same properties as the class will always fail.
So if we were to initialise a class like this:
Then the user can only instantiate it:
Although there is one case where casting to the class will work, which is when the object is an instance of the class and as such contains the class prototype i.e. (using
force as
in this example to avoid nullability):Anon-Objects [Compiler Infered]
Anonymous objects are special types in Kipper, as with TypeScript, the user shouldn't be expected to create an entirely new interface for every single object they want to define. These anon-objects are unmodifiable types which are inferred for a specific object and will be set as the variable type if the user for example does:
Type inference will be more thoroughly explained in the next section.
Type Inference against redundant interfaces
An important part of Kipper's type system is type inference, which is required so that the user won't have to define a type or interface every time they initialise an object or array (This was already partially shown in the previous sections).
Type inference also means that objects can be initialised anywhere without limitations, such as when passing arguments to a function, returning a value or similar.
For example:
The same happens with object definitions as well:
When you are now using
x
all the defined properties can be accessed and used, as the type of variable has been set to the inferred type of the object.Any Troubles & Safety conversions
Kipper aims to provide secure types which cover all the edge cases that can't be easily covered in TypeScript by providing runtime checks as well as stricter compile-time checks for types such as
any
or{}
(obj
in Kipper).For example, the following script shouldn't run as it would in TypeScript (Imagine the expression on the right is not just "2", but some other expression returning
any
, like a lot of web JavaScript functions do):To cover this case, the user would need to then cast the expression from
any
tonum
. As the compiler can not determine whether the expression is valid at compile time, expressions such asEXP cast as T
are invalid. Still the user has these two options to cover this edge case now:EXP try as T
, which would attempt at runtime to cast the expression and if the cast is not possible it will returnnull
. That would make it then in our examplenum?
.EXP force as T
, which would attempt at runtime to cast the expression and throw a type error if the expression doesn't work. This expression on the other hand does always evaluate toT
i.e. for our examplenum
with no nullability.Beta Was this translation helpful? Give feedback.
All reactions