-
Notifications
You must be signed in to change notification settings - Fork 89
Generic specifier
There were some other proposals for the syntax. As for the reasons we cannot use <>, we have discussed this some time ago.
Basically this is because of our New Brave, Great Parser. It first handles parens ([], {} and ()) as groups. This allows for far more powerful syntax extensions and embedding DSLs (like XML or regular expressions). Anyway, the parser cannot handle <> as parens, because they are used in regular operators.
There are some other advantages here:
- it is easy possible to write a simple syntax highlighting for types in most editors now,
- the lexical structure is far more regular,
- and there is no need for strange lexer hacks they use in C# and C++.
Why cannot we use just [] for generic specifier? Please consider the following code:
class Bar [T] {
public this [T] () { ... }
}
class Foo {
public Bar [x : int] : void -> void
{ get { ... } }
foo () : void
{
_ = Bar [qux] ();
}
}
Now the invocation of Bar[qux] can have two meanings -- it's either construction of a new Bar instance or a reference to a indexer Bar. You could say that it is regular overloading resolution problem. But it is not. What is problematic is how to parse qux. In regular overloading you can parse and even type-check parameters of a call and then try possibilities. Here it is not possible, even parsing depends on the meaning of Bar.
Therefore while we think this would be the best solution, this cannot be used.
For the same reasons we cannot use () nor {} alone for generic specifier.
As for the Foo[_ : int, _ : string]
there are two problems --
_ : int
is (almost?) a valid expression, so the ambiguity remains,
and the second more important problem is that it looks like we were
forcing types of regular arguments to be of type int and string. This is
not true in general -- there can be for example no formal arguments.
We don't like the new
keyword, because allocation is far more
frequent in the functional languages and we would like to save typing. Beside
it would only solve the problem for constructors, not for generic methods.
One of the proposals was: Foo () : Foo [int,string]
(actually it was Foo[int], but this is not possible,
because type argument can be a tuple). For this to be consistent,
it would have to be: (Foo : Foo [int,string]) ()
, otherwise
you cannot do things like List.Map (some_list, Foo.[int,string])
.
This is however still problematic -- when you see :, you don't know if it's a generic specifier or a type enforcement. Heuristic could be used -- if there are two Foos in a row, it's a generic specifier.
What I don't like with this approach is that it is quite lengthy -- you
have to repeat the function name. It is also not immediately clear
what does it mean (that it is not a type enforcement). Maybe
(Foo : [int,string]) ()
would be a better option?
The type enforcement can be also used to make compiler know which type we want, so type inference can guess the proper generic parameters. This is however not true in general. There can be a function like:
SomeFun[T] () : void
{
System.Console.WriteLine (typeof (T));
}
Nemerle syntax comes mostly from C-like languages. This is one of our goals -- make it readable for C programmers. Using just whitespace or using foo.at(1) for indexing is clearly against this goal.
Using prefix generic specifiers also doesn't help readability here. Especially when we use postfix specifiers for types.
What I would personally like to see here is some syntax experiments -- different pluggable parsers/lexers and so on.
This is very sane idea ;-) We could for example use
Dictionary [: int, string :]
or something like this.
But:
- generic types are used very often -- single characters parens are a much better choice here (this is however not true for generic specifier, which is not going to be used often)
- there are already some .NET languages using [] for type parameters,
- we already changed the generics syntax twice, I don't think it is that good idea to change it again.
Probably the set of acceptable solutions here could be limited to using some keyword or operator in front of parens (or just after the first paren), preferably [], because they are already used for generic types. Of course other solutions are possible but keep in mind the arguments stated above.
Proposals include:
- .
- of
- with
- =
- :
As of Nemerle v0.9 the dot notation won and it seems that the language will stick with that. If you cannot understand what is this dot notation then look at some examples.