Overview
Types Intro
24 min
shift plays around with a few differents of type theory there are 3 major ones to understand values types kinds this section assumes the reader understands some basics such as what is a variable and the difference between a string and an integer this is not a good place to learn programming if you cannot code that said it will walk through what typing is incase you never really learned it and/or cargoculted through the concepts in your career overview i am in no way an expert on type theory, so the explaination above is how i've wrapped my head around the concept, not a phd's mathematically explaination i am sure there are gaps and easy holes a mathmatician could poke at this never the less; my lack of nomeclature and proper prose around type theory does not make the concepts being conveyed invalid, just unprecise compared to academia at the bedrock of the language we have the axiom data structures building blocks that all other structures in the language are built out of these are sometimes called the primitives, built in, seed, or standard types for shift anything that can be repersented on its own as a literal, as the primitive types of the system as a programmer in virtually all languages there are a few you're most likely familiar with numbers flags text byte i intentionally used a more human friendly name just to demostrate that virtually all langauges have these sometimes the language is very sparse and gives you just byte with it's 0 to 255 possible values othertimes it's nice and presents a good foundational set of values to work with all data is repersented by this and/or combination of bytes shift's supports all of the primitives that c# does below are the most foundation ones bool/bit a value containing 0 to 1 array a fixed length collection of something byte an array of 8 bits giving 0 to 255 values which triggers the chicken egg question of some tautological definitions int char string the primitive building blocks are bootstrapped by the language we may be able to express them in terms of the langauge or other building blocks, but they are axiomnotic we aren't going to get into bootstrapping computing and computer science bedroom i promise to move higher level to something more applicable and useful from here out, but the context is important those closer to some of the more subtle parts of c# may understand that the above primitives are sometimes value types and sometime reference types there is no such distinction in shift so if you're aware of it in c#, throw it out mentally, and if you aren't we'll come back to how it behaves values in shift the first type universe is a value if you aren't familiar with the term type universe then mentally replace it with type tier tier of defining a data's structure/form, how information is formed in the language some common values 3848547 an int "why howdy partner" a string false a boolean see docid\ mwkykvjstikz2sxsvln1i for an exhaustive list of primitives these value value s are what we would call constants the value is 100% definable at compile time and is immutable it is constant it's in definition and what it would e valua valua t e e to a value can be assigned to a variable in math more generically we may call this a term var foo = 32; it's called a variable because it's a term whose value may vary by that, we mean that the variable will store/hold/reference/point/contain different values at differnet points in time; however it's easiest to think of, and this isn't low level so the distinction isn't important the value itself never changes the number 32 is always 32 but the term foo may have a different value at later points in time foo = 7;//now it contains the value 7 foo = 0;//now it contains the value 0 foo = 32;//now it's back to it's original value of 32 var bar = "honk"; bar = "quack"; bar = "oink"; foo varies over time, or at least has the potential to hence why we call it a variable at least why i call it one ) 7, 0, and 32 share an interesting property and so do "honk", "quack", "oink" these sets belong to different types the first being an int32 and the second being a string types a type can be thought of as a template for a value it's the definition, constraints, contract of configurations or possibilities that value will have a non exhausitive list of some primitive types boolean boolean will be either true true or false false char char will be a 16 bit unicode character ranging from u+0000 u+0000 to u+ffff u+ffff int32 int32 is 2,147,483,648 2,147,483,648 to 2,147,483,647 2,147,483,647 uint64 uint64 is 0 0 to 18,446,744,073,709,551,615 18,446,744,073,709,551,615 string string is a fixed length array array of char char blue is the name of the type and yellow is the range of values it can be this range of values is the contract int32 guarentees is never be larger than 2,147,483,647 2,147,483,647 also string helps demostrate that a type's definition can be a combination of other types all of other types are too, if we really get into the bedrock but not a discussion for here because everything is derived from binary, a type is a contract of what combination of 0s and 1s will appear since a type can be defined as a combination of with other types, we use these abstractions to build up to more meaningful repersentations of our work for a type if you mentally imagine enumerating, loop through, all possible combinations you end up with a collection of values that match that type for byte this would be 0 255 normally we think of this in the other direction for a value, what collection does it belong to? that is the type of the value take the value 0 0 is this a bit/flag/boolean, a byte, an integer? if it's an integer what size 16, 32, 64 bit? is it signed or unsigned? is it floating point, double, or decimal? is it ascii character 0 or a string of text that is just 1 character 0? in shift, like c#, each primitive type has a different way of coding it's value in source code this helps remove ambiguity 0 0 is an int32 int32 see the docid\ mwkykvjstikz2sxsvln1i page for an exhaustive list var foo = 0; since 0 0 is an int32 int32 , that means variable foo's type is the same as the value's it's being assigned's type from here on, we know that foo will only ever contain a value that is within the collection of possible values that the int32 type allows var foo2 = 3f; this statement would supply foo2 with a floating point number 3 ( float float which aliases single single ) var foo3 = "3"; finally this statement would supply foo3 with a string string containing a single char char 3 just to help demostrate similiar looking things, something that we may even say outloud identically in english; it's just 3, have different types in shift's type system type inference when declaring a variable for use, you have to specify the type that the variable will be the syntax for this is as follows { type type } {identifier} so far all of the examples above have used var as their type in this syntax this is not a type it's a placeholder for a type that the compiler will supply you can imagine it as whatevertypethecompilerdeterminesiscorrect whatevertypethecompilerdeterminesiscorrect foo = = 7 7 ; the compiler determines what to supply by looking at the incoming value on the right side of the = = operator whatever type this value is, is the value that compiler uses for var in our above case since the value is 7, it's type is an int32 that mean's foo's type will be int32 a programmer can explicitly write this as int32 int32 foo = 7 7 ; the reason most variables are typed with var instead of their explicit type is it is redundant since the compiler can determine the variable's type via the supplied value, it does not need to be supplied on both sides this act of determining the type via the supplied value is called type interference the static type is inferred from other parts of the statement type inference can only be used when the type is deterministic and unambiguious here are two common examples where the intended type cannot be inferred var foo; when declaring a variable and taking the default value instead of using an initializer the type must be specified this is because there is nothing on the right side of the = to infer against you must instead write int foo; to be able to declare an integer variable without an initializer second example var foo = someflagvariable ? "i am a string" 37548 ; the initializer here is ambiguious on what type the returned value will be it will be either a string or an int32 in this case you would need to specify that foo is a type union, pick a common base, or refactor your code so that there is not two types example of what a type union in this situation would look like as a possible solution string|int foo = someflagvariable ? "i am a string" 37548 ; see docid\ lgos1gypjssl243cmms1o for more information type safety shift is both statically and strongly typed what this means is static the type of something cannot be changed once created strong operations between things will enforce constraints related to types shift provides type safety at both compile and runtime it is a major philosophical goal of the language that as much as possible occurs at compile time, so you get fast feedback on errors the primary goal of type safety is to ensure you do not attempt to change the type of something or break a constraint below is a trivial example var foo = 32;//foo is an int (int32) via type inference foo = 27;//it can be assigned a new int value at any type foo = "honk";//the compiler throws an error on this line the compiler throws an error on the 3rd line because foo is of type int and the assign valued is a string type safety will ensure that any interactions of types and values match their destination target this is useful for ensuring the wrong thing isn't assigned to a variable or passed into a method as a developer you can rely on the types being whatever they're declared to be complex types in constract to the primitive types, whose values can be coded as literals, there are complex types these are types that are can only be expressed and programmed as a combination of other types these types can be both primitives or other complex types at the end of the day all complex types ultimately terminate in primitive types with literal repersentations complex types have no literal repersentation to create assign a value of a complex type you need to create an instance let's look at a simple complex type; heh oximoronic shift record persondata { string name; int age; //constructor this(bool isadult) { age = 21; } //another constructor this(string name, isadult) (isadult) { name = name; } } var a = new persondata(); console writeline(a);//persondata console writeline(a name);//"" console writeline(a age);//0 the new keyword allows us create an instance of a type this creating an instance, instantiating, a type produces a value of that type drawing attention back to literals the compiler supplies bootstrapping the instantiation when using the new keyword you supply it the desired type and constructor overload note how name and age have no value assigned to them they use their own type's default constructor for string (string) this is "" "" (empty string) and for int (int32) this is 0 0 constructors constructors are special methods that are first run or initializer blocks of code for setting up/configurating complex types they are defined by a method with no return type and a name of this you'll also notice that inside of the method there is no return statement both the return type and returned value from the method are inferred and system supplied you imagine the system supplied method under the hood looking something like this persondata this(bool isadult, persondata self) { self age = 21; } repersentative record types are immutable and can only be `in flux` inside of their constructor once the method has been initialized it's locked in whatever state it is so you would not be able to pass a persondata into a method and then modify it; in this way the above way is repersentative on the flip side, it is bootstrapped magic that allows a constructor to modify a record's values inside of the constructor there is no syntax that morphs the record from modifiable to unmodiable or back this would be an interesting way of making the language more complete mathematically every type has a system supplied default constructor that takes no arguments a user can define extra operators with any number of operators they want, as shown back above in the record's definition with the 2 different operators var a = new persondata(); this uses the () constructor instance, which is not defined on in persondata this is uses that system supplied constructor as mentioned a user can also supply their own default constructor implementation let's update our record's definition shift record persondata { string name; int age; this() { name = "joe smith"; age = 54; } } var a = new persondata(); console writeline(a);//persondata console writeline(a name);//"joe smith" console writeline(a age);//54 you can assign initial values to fields directly to help reduce code too shift record persondata { string name = "joe smith"; int age = 54; } var a = new persondata(); console writeline(a);//persondata console writeline(a name);//"joe smith" console writeline(a age);//54 finally remember what we learned from type inference the reason that variable a is a value of type persondata is known via type inference the new command returns a value of whatever type it is given the compiler inspects this when inferring the type for var we mentioned a few situations where one may be required to specify the type explicitly instead of use var there are also times as a programmer you may prefer to use an explicit type over type inference purely out of convenience; even if the compiler could correctly infer a type the primary case for this is when your variable will be taking the default value for a type shift //instead of var a = new persondata(); //a cleaner and terser persondata a; for those coming from many object oriented languages it's important to draw attention to the differences in behavior for the second statement there is no such concept as null or undefined in shift everything has a fully initialized default value if no initializer is provided, a system supplied new type(); is supplied finally there is something called initializer syntax, where the fields of a type can be assigned inline alongside the new statement var a = new persondata { age = 28, name = "honk bob 5000" } when using initializer syntax you cannot supply a constructor invocation hence the lack of () anywhere furthermore the fields and their assignments can be any order, it is not critical that they are in the same order as they appear in the type complex types continued as mentioned a complex type can contain another complex type let's expand our example record persondata { string name = "joe smith"; int age = 54; } record morecomplex { persondata boy; persondata girl; this((string boy, string girl) names, int age) { boy = new persondata { name = names boy, age = age }; girl = new persondata { name = names girl, age = age }; } } var foo = new morecomplex { boy = new persondata { name = "jack", age = 8 }, girl = new persondata { name = "jill", age = 9 } } var bar = new morecomplex(("sid", "sydney", 15)); showing off use of nested complex types with both initializer and constructor invocation kinds the rules governing what can be placed into a type and the mutability vs immutability after it's creation are governed by the type of type it is this collection of types is known as kinds there are a few system supplied kinds in shift record immutabile datastructures that can only be comprised of other records all primitives are records structure mutable datastructures that can be comprised of both structures and records library a singleton collection of named methods with shared immutable state unlike the other kinds, a library produces a value and not a type service dedicated to business logic it's fields are immutable but can contain any other kind of type you can think of the fields are bound scope for the instance of the service enum a named list of values technically this is liked an instance of an immutable dictionary\<symbol,t> interface a contract that record, structures, and services can implement at this time there are no user defined kinds type typearray = t\ array\<s\ int,type>; //var parameterarguments = \[int, bool, string]; //inferred as array<3, type> {int, bool, string } //typearray ta = parameterarguments;//since it's just an alias to this type structure tuple\<ta\ typearray> { this(tuple\<ta> values) { //in practice the tuple type is seeded //if you think about it, you must pass in a tuple of values to a tuple to start it //that passed in tuple's type signature much matter the new instance of the tuple //in this way tuples, are elementary like arrays //we can express their behavior and signature within the langauge } } type parameterset = t\ tuple\<ta\ typearray>; //really complicated way of saying it's an instantied instance of a tuple //with these specific values in this order //for example //var tuple = new tuple<\[int,bool,string]>(3, false, "honk"); //var tuple = new tuple\<ta>(3, false, "honk");
