Description
Summary
Jake cannot support TypeScript's generic call syntax (id<u32>(1)
) due to how ambiguous it is. It is unresolvable by a conventional LR(1) parser. I have come up with three solutions, and this RFC is for either voicing approval for one of them or suggesting other solutions.
Solution 1: the Turbofish
The turbofish id::<u32>(1)
is present in Rust's syntax and generally considered an eyesore. Rust usually handwaves this away as syntax that doesn't occur very frequently or something that just has to be lived with. Furthermore, the ::
doesn't really belong in Jake's syntax, seeing as though it appears nowhere else. Replacing it with, for example, id.<u32>(1)
may be awkward. A pro of the turbofish is that it follows the precedent of a major language and it can be easily transferred from knowledge of Rust.
Example
function id<T>(x: T): T {
return x;
}
id::<u32>(1);
Solution 2: Dependent Types
Generics are in fact a special case of dependent types (the dependency being a kind, represented by *
). Thus, this solution might make the most theoretical sense, but feel completely foreign to a new user. In a nutshell, types are supplied as regular parameters to functions, and other parameters use that parameter's name to refer to the type. Dependent types will be implemented, but it's not estimated to be any time soon, and generics may as a result be delayed by the same amount of time.
Examples
function id(T: *, x: T): T {
return x;
}
id(u32, 1);
Cons with this method include confusion if types look like terms. For example, if Jake had values-as-types:
id(4, 4); // identity takes two parameters?
// in reality, the first `4` is a type, while the second is the actual value
Solution 3: Type Ascription
This solution may be the simplest and easiest to understand, however, it may conflict with other parts of the language. This conflict is described after the following example. This feature has actually been accepted into Rust for over half a decade, never being stabilized.
Examples
function id<T>(x: T): T {
return x;
}
id(1): u32; // 1 is coerced to be u32 to fit T
// or
id(1: u32); // T is inferred to be u32 to fit 1
Conflicts
Jake was initially intended to use canonical pattern matching in much the same way as Rust, but to be more familiar to TypeScript users and less noisy in general (i.e. no need for if let
), the current Jake spec uses the same syntax for "flow-sensitive" pattern matching. Should this solution be accepted, this conflict would have to be resolved in some way.