Method
Actions do not exist in Shift. Better said the type Action is an alias to a method that returns void.
type Action<TypeArray> = Method<TypeArray, void>;
TypeArray is a dependent typed Array of Types
It's definition is as follows
type TypeArray<S:int> = Array<Type,S>();
See dependent typing for more information
void is a keyword alias to type Void.
type void = Void;
void being a keyword is always available and guarenteed to be the same type. This is the same as C# and it's keyword type alias. int vs Int32
Void itself is a type alias to a type dependent empty array.
type Void = Array<0>;
Bringing these 2 concepts together gives us Action which is a type alias to a Method with a type argument for input, and a fixed output of type Void which is just itself Array<0>.
Philosophically this helps unify methods with output and no output. There is always input and there is always output. EmptySet might idealogically be the value of either of those two dimensions.
Action and Method both has keyword supplied type aliases for scoping convenience.
type action = Action;//Always in scope, and takes first precendence in type resolution
type method = Method;//Always in scope, and takes first precendence in type resolution
Methods
Taking the above information, all method definitions in constructors are syntactic sugar to a method field.
Method is a record type and is immutable.
Some example methods in their common format you'd experience in C# and then the field syntax.
int PlusTwo(int foo)
{
return foo + 2;
}
//Identical behavior/mechanics as above, just a different syntax for writing
//Long hand lambda syntax for block definition
method<int,int> PlusTwo = (int foo)
{
return foo + 2;
};
//Short hand lambda syntax is supported for trivial blocks
method<int,int> PlusTwo = x => x + 2;
//Type inference on both the input and output of the method
method PlusTwo int x => x + 2;
//Separate example just to demostrate multiple parameters
//Type inference infers method to be Method<[int,int],int>
method AddTogether (int a, int b) => a + b;C# developers will find the initial syntax most common and for complex methods the most commonly used.
Some examples of void return types from C# applied to Shift
//Example 1:
void SomeSideEffectMethod(ComplexType foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
}
//Example 2:
action<ComplexType> SomeSideEffectMethod => (SomeSideEffectMethod foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
}
//Example 3:
action<ComplexType> SomeSideEffectMethod => (SomeSideEffectMethod foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
return;
}
//Example 4:
action<ComplexType> SomeSideEffectMethod => (SomeSideEffectMethod foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
return new void();
}All 4 examples are the same, just using different syntax and amounts of inference. The first form is the most likely form you'd see such as thing in a service. Very similiar to C#.
Unpacking what's going on.
Just like before with the method examples; Example 1 is syntactic sugar for defining an action field. Hence the only diffference between 1 and 2 being the syntax.
It's common in C# when having a void return type to use no return statements. This is shown in Example 1 and 2. But in Shift EVERYTHING has an output, which means there is always a returned value.
The compiler infers that when a void return type is specified that the final line of the block will return a new instance of void.
Furthermore the compiler understands that a return statement without a value, is being asked to return a new void;
Array's are record types in Shift. This means they have equality checking based on their contents, not their instance identifier.
var output = SomeSideEffectMethod(foo);
var emptySet = new Array<0>();
Array<0> bar;//Showing off type default values.
var areEquivalant = output == emptySet
&& output = bar
;
var sameType = output@ == emptySet@
&& output@ == bar@
;
Console.WriteLine(areEquivalant);//Outputs trueVariable bar was thrown in to also demostrate the equivalence of type aliases. void and Array<0> ARE the same type. Void just an alias. This means anywhere you could use the alias you can use what the alias is defined as.
The core take away and thing to remember is a type alias is no inherietence. Void does not inherit from Array<0>, it IS Array<0> with a different name for type resolution purposes.
Some more examples hammering home the type alias versus the definition are interchangable.
//Example 5:
action<ComplexType> SomeSideEffectMethod => (SomeSideEffectMethod foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
return new Array<0>();
}
//Example 6:
action<ComplexType> SomeSideEffectMethod => (SomeSideEffectMethod foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
var emptySetToReturn = new Array<0>();
return emptySetToReturn;
}
//Example 6:
action<ComplexType> SomeSideEffectMethod => (SomeSideEffectMethod foo)
{
var json = JsonConvert.Serialize(foo);
File.WriteAllText("./saveMe.json", foo);
Array<0> emptySetToReturn;
return emptySetToReturn;
}