Overview
Core Concepts
16 min
this is a high level overview of the major differences between shift and c# it also notes somethings that might be identical or similiar just because their major concepts some ideas/concepts may be mentioned twice because of it's interaction with other concepts check out docid\ ojkdtggws dwmdfqdq6wg for things up in the air + thoughts around why i'm on the fence about it clr binding all net/clr assemblies can be consumed in shift shift will expose those types in a shift equivilant this means the vast majority of the existing ecosystem is usable, shift isn't starting from scratch furthermore the binding is bidirectional shift compiles to a clr assembly meaning that a shift project/library can be consumed by any net target langauge this means if you want to start using shift, you can start with your next library or project being in shift, not requiring you to abandon your previous codebase type dependence one of the showcase features of shift is full support for type dependence when defining argument types for generics you can signify a dependent type via the operator shift record myrecord\<t, size\ int> { t foo; int addtosize(int bar) { return size + bar; } } above you can see for record myrecord that there are two type arguments t and size a type argument when accompanied by the operator then supplies the type of the value that be passed in what this means is that size will not be a type passed in, but instead an int unlike compared to a constructor taking an int in, this is a part of the type's definition anything that compares types will know that these are not the same let's look at instatiation of the type shift var myrec = new myrecord\<giraffe,7>(); var rec2 = new myrecord\<giraffe,30>(); var sametype = myrec@ == rec2@;//false console writeline(myrec);//myrecord\<giraffe,7> console writeline(rec2);//myrecord\<giraffe,30> //default tostring for a value is uncertain atm, so above is just an example console writeline(myrec\@ size);//7 var together = myrec\@ size + rec2\@ size; console writeline(together);//37 furthermore let's look back to t it's the normal type argument syntax that most languages use for generics in shift it's using type inference, as there are actually no type arguments that aren't type dependent in long form the record's definition really is record myrecord\<t\ type, size\ int> the default constraint for a dependent type is to be a type when interacting with how types behaves and are accessed this allows you to pass giraffe as the first type argument when declaring the new type checkout docid\ c tijqcmaspzqpr12kpdu for a deep dive into the type dependence system types all type definitions are also namespaced symbols for accessing that type this means there is no need for the typeof operator a type and a reference to a type definition are unified in shift onsequently the typeof operator doesn't exist as it serves no purpose var inttype = typeof(int);//c# var inttype = int;//shift fails in c# as an invalid statement var list = new list\<int>();//c# var list = new list\<typeof(int)>/();//shift if the typeof operator wasn't removed //more importantly var list = new list\<inttype>(); dependent type arguments can be supplied a constant or a variable since all regular type arguments are just inferred dependent types (t\ type) that means all type arguments can actually be variables this is what allows the last line in the above example work shift var foo = new myrecord\<giraffe, 0>(); var zero = 0; var foo2 = new myrecord\<giraffe,zero>(); var giraffetype = giraffe; var foo3 = new myrecord\<giraffetype,zero>(); var biggiraffe = new giraffe(size mega); var foo4 = new myrecord\<biggiraffe@,zero>(); //shows off the @ operator var sametype = foo@ == foo2@ && foo@ == foo3@ && foo@ == foo4@ ;//true they're all identical type the above are only the same type because int is a record, and uses value equivilance if a type with reference equivilance is passed in, then see docid\ jvndfq5gkwsoepmi2sfyz for more information about types there are a lot of interactions and implications that are explored there type aliases shift supports type aliases as a convenience for defining types type aliases can be any combination of open and closed generics in it's definition shift type intlist = list\<int>;//closed generic example type listalias\<t> = list\<t>;//open generic example type stringkeyeddictionary\<t> = dictionary\<string, t>;//mixed open and closed type inferredtypeargs = dictionary\<k,v>; when defining a type alias that points at a generic type you only need to specify the type arguments if you want to change the ordering of types or partially close the definition otherwise you can drop the type arguments from the left and the compiler will infer based on the right inferredtypeargs uses type inference to actually be of type inferredtypeargs\<k,v> see docid\ augjmccvisx5gmhmrf4sc for more information null does not exist you cannot a null to a variable reference types do not initialize to null and there is no way to get them there c# reference types are either initialized to a non null default state or wrapped in nullable the type nullable's value property behaves different than c# it will returned a fully initialized instance if the wrapped type is null action, func, and void methods take the place of delegates, actions, and funcs in shift they've all been unified into this one record type check out docid\ gdytda2qp1ivc7i30nt2n for more info the closest thing to an action is a method\<t, void> which is a method that returns a void a void (keyword alias to void) is a type alias to array<0> checkout docid\ mwkykvjstikz2sxsvln1i for more information about void checkout docid\ c tijqcmaspzqpr12kpdu for more information about array<0> constructors constructors in shift are syntactic sugar for methods checkout the docid 8cur6rtdh4zhi8fdh6vho page for more information in general it's a method that is handed an already instantiated instance of the type and returns the type structure basicstructure { int a; int b; this(int a) { a = a; } } //the constructor is syntactic sugar for extend new\<basicstructure> with this(a, basicstructure self) { self a = a; return self; }; constructors actually belong to the standard library supplied new class explained below in the new section see docid\ xqe2gxwiolp1cq3erqddr under discussions for some ongoing thoughts about the area new when you type var foo = new goose("honk honk"); what is going on in shift? it's not the same as c# this is syntactic sugar for new\ construct\<t>("honk honk"); so new goose ("honk honk") will bind to the new library (standard library supplied) and supply type argument goose, and then 1 string variable you can imagine the insides of the new library being something like this t construct\<t,parameterset>(parameterset arguments) { var fullset = arguments + default(t); //tuple + operator produces a new tuple //with (leftterms rightterms) return this@ methods where(x => x name == \ this) oftype\<method\<fullset\@ta,t>>//only methods that are the right constructor single (fullset) ; } break down the do notation line by line first statement we need to build a new type array that is the passed in arguments + the type being instantiated this comes in handy during the second statement we do this by passing in the arguments + a default instance of t this is also where t is allocated into memory second statement get a reference to the library type itself the @ operator is syntactic sugar for gettype() retrieve the collection of methods filter to only methods that have the this identifier in this context is the syntax for defining an identifier literal this is similiar to the symbol operator in some languages filter to methods that match the right method syntax methods that will return the type we're constructing methods that have the exact match of parameters passed into this function specifically the parameters + the instantiated type since it's the final argument in the constructor always fullset@ is getting the type of fullset and then we're accessing the typearray ensure only 1 result came back the compile is fucked if this throws an exception invoke it and pass in the arguments end statement, which passes this r value to the return command which bubbles up out of the block it's worth noting that this isn't the literal implementation but supposed to be repersentative of what is happening something somewhere has to actually allocate memory and get a reference to pass to the constructor interference local scoped variables can use var just like in c# when declaring variables, the default constructor is always inferred or more accurately there is no null value for reference types, and everything has a default value that is non null there is always a system supplied constructor of () for every type namespacing namespaces are inferred from relative path to folder root namespace is the project name namespaces can be explicitly defined via namespace { } which scopes everything inside c# style namespaces do not need to be imported to be used the compiler will infer which using statements and only throw an error when ambiguity exists using statements to pull a namespace into local scope are necessary if a type is ambiguious based on the linked libraries using statements can be placed top level in file, scoping the entire file in a construct scoping the entire construct in a block scoping just the block access modifiers in shift there is no scope modifiers there is no private, protected, internal, public everything acts as though it's public check out docid\ x4bnd9vaxnludm4t3u1 1 as to some thoughts on why i'm very aware that this is chucking a bundle of dynamite on encapsulation each type class (structure|record|library|service) is accesible from anywhere and all fields can be accessed from anywhere this is intentional to force everything public and reduce how much boilerplate there is when defining types and aspects readonly similiar there is no ability to mark something readonly all fields are readonly except for structure fields there are no modifiers to vary this
