You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The goal of this document is to expand on the ideas laid out in the 0.1 proposal, to slowly work towards a complete language. Programs in the 0.1 proposal could already do simple calculations. While this proposal won't extend the capabilities much, it's working towards a language that is able to build up abstractions with user-defined types and generics.
Scope of the features
The primary topic of this proposal will be the type system:
Generics
Overloading
Type inference
User defined record types
For loops
Just like last time, the proposal implicitly defines the initial syntax.
Generic functions
Generic type parameters can be introduced after the function name between [...]. For example:
func second[T, U](x: T, y: U): U = y;
The rationale for leaving <> is that they are binary operators, which can really complicate the compiler in very undesirable ways - see what C++ goes through while parsing, or what Rust introduces syntactically not to make it painful. The simplest is just to take an operator that already comes in pairs, and [] is already used by languages like Scala.
Function overloading
While functions were already proposed in 0.1, function overloading was unspecified. I see no reason to disallow it, overloading should stay. A concrete function signature should bind stronger than a generic one. For example:
func foo[T](v: T): T = v; // (1)func foo(v:int): int = v; // (2)func main(){vara=foo(true); // (1) is calledvarb=foo(1); // (2) is called}
This should be simple enough, but once subtyping comes into play, the rules might be complicated, we should keep that in mind. With the current rules we can already produce ambiguous calls - meaning it's not too hard to overcomplicate this system:
func foo[T](x: T, y: int): int =0; // (1)func foo[T](x: bool, y: T): int =0; // (2)func main(){vara=foo(true,1); // Both (1) and (2) match the 'same amount'}
Type inference
One of the main strengths of the language should be a way stronger type-inference than what C# allows. A good example on a permissive - but not ML-level - type-inference system can be found in Rust: signatures must be fully typed, but inference can work freely in the function-local scope.
Return type inference
Functions with inline expressions should allow return-type inference:
func f1(x:int)= x; // OK, inferred to be (int) -> intfunc f2()= Console.WriteLine(""); // OK, inferred to be () -> unitfunc f3(){// ERROR: functions with a body need explicit return type,// assumed to be unit otherwisereturn1;
}
Variable type inference
Variables can be declared without type, even when they do not get assigned a value immediately:
varx:int=4; // OK, explicitly typed, value matchesvary=4; // OK, inferred immediately to be int from the valuevarz:int; // OK, explicitly typedvarw;
w =1; // OK, inferred to be int from usagevarq; // ERROR: Could not infer type of the variable
Generic type argument inference
When a generic function matches the best for a function call, the generic types would be inferred, no need to specify call arguments, just like in C#:
func foo[T](v: T): T = v;
func main(){foo(3); // T = int}
The generic arguments can be explicitly specified too, in case it can not be inferred (or for explicitness):
func foo[T](v: T): T = v;
func main(){foo[int](3);
}
Type placeholder
The _ can be used as a placeholder type, which can be useful when working with generics, only wanting to specify some of the type arguments:
I believe a single for-each should be fine, if we can ease the range-creation a bit. Something like:
for(iin range(0,10)){
WriteLine("Hello, "+i);}
The type could be specified after the variable name, like for (i: int in range(0, 10)) .... The variable would be a val, meaning it can't be reassigned.
Under the hood, it would be desugared into a while-loop, similarly to C#:
for(iin range(0,10)){
WriteLine("Hello, "+i);}// Becomesvarenumerator= range(0,10).GetEnumerator();
while (enumerator.MoveNext()){
val i = enumerator.Current;
WriteLine("Hello, "+i);}
Future ideas
Ideas that came up while writing this proposal:
named arguments for function calls
named explicit generic arguments
optional arguments (maybe even with non-constant expressions?)
optional generic type arguments
The text was updated successfully, but these errors were encountered:
Goal of the document
The goal of this document is to expand on the ideas laid out in the 0.1 proposal, to slowly work towards a complete language. Programs in the 0.1 proposal could already do simple calculations. While this proposal won't extend the capabilities much, it's working towards a language that is able to build up abstractions with user-defined types and generics.
Scope of the features
The primary topic of this proposal will be the type system:
Just like last time, the proposal implicitly defines the initial syntax.
Generic functions
Generic type parameters can be introduced after the function name between
[...]
. For example:The rationale for leaving
<>
is that they are binary operators, which can really complicate the compiler in very undesirable ways - see what C++ goes through while parsing, or what Rust introduces syntactically not to make it painful. The simplest is just to take an operator that already comes in pairs, and[]
is already used by languages like Scala.Function overloading
While functions were already proposed in 0.1, function overloading was unspecified. I see no reason to disallow it, overloading should stay. A concrete function signature should bind stronger than a generic one. For example:
This should be simple enough, but once subtyping comes into play, the rules might be complicated, we should keep that in mind. With the current rules we can already produce ambiguous calls - meaning it's not too hard to overcomplicate this system:
Type inference
One of the main strengths of the language should be a way stronger type-inference than what C# allows. A good example on a permissive - but not ML-level - type-inference system can be found in Rust: signatures must be fully typed, but inference can work freely in the function-local scope.
Return type inference
Functions with inline expressions should allow return-type inference:
Variable type inference
Variables can be declared without type, even when they do not get assigned a value immediately:
Generic type argument inference
When a generic function matches the best for a function call, the generic types would be inferred, no need to specify call arguments, just like in C#:
The generic arguments can be explicitly specified too, in case it can not be inferred (or for explicitness):
Type placeholder
The
_
can be used as a placeholder type, which can be useful when working with generics, only wanting to specify some of the type arguments:The
_
essentially means to create a type variable that will be inferred by the compiler. It can be used in any context, just like any other type.Incomplete inference
An incomplete inference will result in an error. For example:
Any type is considered incomplete, that contains type variables.
User-defined record types
See #41 for the design documentation.
For loops
I believe a single for-each should be fine, if we can ease the range-creation a bit. Something like:
The type could be specified after the variable name, like
for (i: int in range(0, 10)) ...
. The variable would be aval
, meaning it can't be reassigned.Under the hood, it would be desugared into a while-loop, similarly to C#:
Future ideas
Ideas that came up while writing this proposal:
The text was updated successfully, but these errors were encountered: