```csharp= //Normal C# argument X must be nominally int public void Foo(int x) //With claims constraints public void Foo(int x) where x < 10 //You can't call the function without providing claims that //meet the constraint Foo(5)//Literal is compile time known to meet it Foo(15)//Fails Foo("5")//Not an int var bar = 7; Foo(bar);//Passes because the literal claim carries forward bar = Random.NextInt(); //Assignment wipes the known claims. Foo(bar);//Compiler error because it doesn't have the claims if(foo is < 8) { Foo(bar);//Allowed now that we have the claim //{<8} claim satisifies the {<10} requirement } //^This is all what was previous shown, now for some unifying //This syntax also for you to define claims or sets of them once //And then reuse them type IntLessThan10 = int < 10; object bar = Random.NextInt(); Foo(bar);//Complains because it's both not an int or proven <10 //This is valid C# 11 pattern matching if(bar is int < 10) {//It's a type clause with a relation clause Foo(bar);//Is proven now } //But we should be able to write this purely as a type clause if(bar is IntLessThan10)//1 clause matches the type {//The type itself has its own constraints to handle Foo(bar);//The type has universal claims of being an int and < 10 //By universal, I mean 100% of the type an object of that type //Always has these properties } //That also means we could refactor our method to look like public void Foo(IntLessThan10 x) //The entire constraint can be part of the type signature. //A consequence of this is that when you use where syntax to //Define a constraint inline, you're just creating an anonymouse type type A' = int < 10; //Another implication of this is that the type clause from pattern //matching should also be liftable into the where clause on the method //This is already supportable //We could write the least reusuable, most verbose version of the //method like this public void Foo(object x) where x int where x < 10 //All 3 with the object & 2 anonymouse clauses making anon type //the type upfront + 1 clause making anon type //the named type //They all express the same thing. It's purely named for anonymouse //typing for the method signature. Both are as strong and static //This also means that we can produce values with known claims //This means you don't have to constantly recheck via if statements //If the value coming out of a method has a guarenteed value. type PositiveInt32 = int > 0; public class Array<TValue, PositiveInt32:Size> {} public PositiveInt32 RandomFromCeiling(PositiveInt32 ceiling) { return Random.Next(1, ceiling); //Returns a range claim of 1-ceiling which means the >0 } var foo = new int[7];//Valid for the Size, since 7 is a PositiveInt32 var foo = new int[0];//Invalid since it's wrong var bar = RandomFromCeiling(85); var foo = new int[bar];//Guarenteed to work //RandomFromCeiling produced a value and we haven't interrogated it //yet to learn any of its properties, but the type system tells us //A lot about it. The Bounded Type of PositiveInt32 tell us that //It's type must be Int32 and that it always passes the > 0 clause //Normally the type system only tells us the type, Bounded Types //Explands that to allow us to ensure specific states of types without //Needing to manually create a duplicate class for each combo //IE in old C# you'd need to do public struct PositiveInt32 { PositiveInt32(int value) { if(value is not >0) throw new Exception("Runtime failure"); } } //This has all sorts of negatives //You cannot inherit from a struct (for good raeson) //so there is no nominal relationship between PositiveInt32 and int //This requires jank where you have an IInt interface that both the //int struct and PositiveInt32 struct implement //Now somewhere that takes an int in, instead is taking the interface //Which nows requires 100% of interactions incur boxing and unboxing //Overhead which involves your pass by value items now generating //memory pressure via GC //Also there is no mechanism in C# to ensure you never instantiate //a PositiveInt32 with an invalid value. So all of your compiler issues //Will occur at runtime //^All of the aforementioned downsides are large enough on their //own to be an immediate no for the C# team. //Type bounds have none of these types. type BoundedType = int;//This bounded type is just a type alias //^ Literally type script's syntax for it //x doesn't have a nomimal relationship with int, because it is an int //^ Specifically it's not a subtype, though worth thinking of if it //shoudld be //this means you can pass variables of type BoundedType into anywhere //That takes an int. You can always pass something with claims to //Something with less restrictive claims that it matches //Also all reasons that the compiler yells at you for passing the wrong //parameters in for an argument is around claims missing //Passing a string parameter to a int argument doesn't fail because //Of C#'s type checking system. It fails because it lacks the int claim //a string variable has the string type claim. //This is in practice identical behavior as the type checking //But it comes from a more powerful base. The type system operates //On both type state and type kind information //Coming back to the Array example public class Array<TValue, PositiveInt32:Size> {} //This is making use of both Bound Types for the allowed value //And Dependent Typing for Size being part of the type //This ensure at compile time that the array is initialized correclty //We can also remove runtime bounds checking from indexing now public TValue this[int index] where index >= 0 where index < Size { //unsafe code to return data since array is kinda fundamental to //memory access } //The compiler knows the exact size of each array //You can never require something outside of the array's bounds //Thus we don't need to spend the overhead checking the bounds //This is over 75% the cost of interacting with an array in C# //An a common thing that devs need unsafe code to turn off for //Performance gains. Instead we lift the problem to compiler time //And now get the safety without any performance cost at runtime //Unanonymous claims aka anonymous bounded types are intended for //temporary and quick checks. The lifetime of the claim is transient //The only guarentee is that at the time the claim was asserted it //was true, were a variable of a bounded type always has those claims //Expanding from our trivial type alias type SomeAlias = string; //To something more useful type DriverScore = int >= 0 and <= 10; //We use primitives all of the time to store data because //A newtype would be overkill. Often times the validity of that data //Is conveyed via some field name that uses the primitive and //Various runtime checks //Bounded types allow us to provide the strong semantics of how //A type is used while leveraging a primitive for memory storage //This can also produce interesting interactions when leveraging //other language features, such as type dependence type IntRange<int:Floor, int:Ceiling> where Floor < Ceiling = int >= Floor and <= Ceiling ; //IntRange has 2 dependent type arguments //int Floor and int Ceiling //It has an anonymous bounded type where Floor is less than Ceiling //This is an int value and there are bounds constrainting the int //To be within the Floor to Ceiling range //Now all of proven things are enforced via the type system var foo = new IntRange<2,9>(5);//5 is correctly between them var foo = new IntRange<2,9>(-3);//Compiler knows this is an error //We can also express these with interfaces that back generic math type Range<TNumber, TNumber:Floor, TNumber:Ceiling> where TNumber : IRelational<TNumber,TNumber,bool> = TNumber >= Floor and <= Ceiling //This lets us create things like type ArrayIndex<PositiveInt32:Size> = Range<0,Size> < Size; var foo = 7; ArrayIndex<10> bar = foo;//Allowed //Explaining why //ArrayIndex is bounded to the type Range<0,Size> and //Has the extra bound that Range<0,Size> is less than Size //Digging deeper //Range<0,Size> is bound to the type int //It has a floor of 0 and Ceiling of Size //Size is a dependent type on ArrayIndex //ArrayIndex<10> means Size of type PositiveInt32 has a value of 10 //The int backing Range<0,Size> is what ultimately backs ArrayIndex //It's value must be inclusively contained within the floor and ceiliing //Effect Floor >= Value <= Ceiling (where value is an int) //Then we add the extra clause that it is less than Size, which adds //Value < Size, plugging the 7 in for comparison to the bounds system //You can imagine the check occurring like int value = 7; int Size = 10;//Capture the dependent type for Size from ArrayIndex int Floor = 0;//Literal passed in int Ceiling = Size;//Passed from ArrayIndex to Range bool fitsInBounds = Floor <= value && value <= Ceiling//Range check && value < Size//Additional bound on ArrayIndex definition ; //While the Range<0,10> accepts 10 as a value //the extra < 10 further slims down the accepted values, so only 0-9 //Are allowed //This means our array element index access method can become even //stronger public TValue this[ArrayIndex<Size> index] => ...; //All instances of ArrayIndex<Size> are valid indexers //To Array<T, PositiveInt:Size> //The first version makes it so any algorithms manually accessing //an array do it safely var size = 5; var elements = new Blurbsk[size]; for(var i = 0; i < Blurbsk.Size; i++) { //We as humans know i should be safe 100% of the time var e = elements[i];//Compiler would yell at us //Currently the claims system cannot enforce this yet //We'll assume that for loops produce no claims //I know it's trivial for us to inspect and decide this currently //But technically all 3 statements in the for loop can be //arbitrary code if(i is >= 0 and < 5)//Now we've proved that i { var e = elements[i];//Now it's proven } } //Using the ArrayIndex type for(var i = 0; i is ArrayIndex<Size>; i++) { //Now we do have a claims being proven to get here var e = elements[i];//We can safely do this } //This means we can also have another method return the address to //something in the array and safely consume it ArrayIndex<Size> GetRandomIndex<TValue, PositiveInt32:Size> (Array<TValue, Size> array) { var rand = Random.NextInt(0, Size - 1); //This always should be valid but the claims aren't there //Should be someway to improve the language to solve this return rand is ArrayIndex<Size>//Only uses this if it meets it ?? 0//Which does always meet it ; } //Now we could grab an element safely like var index = GetRandomIndex(elements);//Type inference fills in args var el = elements[index];//We can safely know accessing this always //Will work //It would also be nice to associative types, so that the ArrayIndex //Can be pulled from the array instance strongly //not constructed by the user via ArrayIndex<10> //Lifting via type dependent should allow a value of the type Type //To be lifted back up into being a type public Array<TValue, PositiveInt:Size> { public Type IndexType => ArrayIndex<Size>; } //We'd want to be able to do something like myarray = new Array<string, 50>(); var i = 8; if(i is myArray.IndexType) {} //Currently it'll return null //Since i is an int, and int != Type //This is made up bad syntax that could fix it //O wait I solved it public Array<TValue, PositiveInt:Size> { public type ArrayIndex = ArrayIndex<Size>; //This nearly is the C# that does this public class ArrayIndex : ArrayIndex<Size> {} } //The type keyword declares the Bounded Type definition //ArrayIndex is namespaced within Array //Array's namespace is local in the above examples //You can imagine via usings statements at the top of some file Array<5>.ArrayIndex//is a reference to the type ArrayIndex<5> //The bounded Type is in the simpliest form where it's just an alias var foo = 3; Array<5>.ArrayIndex bar = foo;//Should work ArrayIndex<5> baz = foo;//Is easier than manually namespacing in //You get a win when you already have an array instance var myarray = new Array<string, 50>(); myArray.ArrayIndex foo = 15;//We know it works myArray.ArrayIndex foo = 90;//We know it wont compile //This scoped (it uses a dependent type from a parent scope) //bounded type saves us having to pass around 50 everywhere for(var i = 0; i is ArrayIndex<Size>; i++) //Becomes for(var i = 0; i is element.ArrayIndex; i++) ```