mlscript
mlscript copied to clipboard
Support argument list and array splices `...xs`
Support typing and type inference for argument list and array splices.
Examples: f(a, b, ...c, d, ...e) and [a, b, ...c, d, ...e].
Type inference should do its best to infer the most precise types, but it may sometimes hit ambiguities, such as when constraining (a, b) <: (...?s, ...?t). In such cases, reporting a type error will be fine, prompting users to write a type annotation in order to remove the ambiguity.
The way I'm thinking to represent that is by using a new type form:
case class Splice(elems: Ls[SimpleType])(val prov: TypeProvenance) extends BaseType
For example, (a, b, ...c, d) would be represented as Splice(TupleType(a :: b :: Nil), c, TupleType(d :: Nil)).
A complication will arise when computing the Conjunct normal form of two such splices. For now it's fine to raise a type error when this happens. (Eventually, we may have to add a BaseType form for arbitrary intersections of splices...)
I don't quite understand this part: Splice(TupleType(a :: b :: Nil), c, TupleType(d :: Nil)), is using TupleType here purely for convenience?
I wonder if it would be cleaner to use something like Splice(Left(a), Left(b), Right(c), Left(d)) @_@
Oh yeah you're actually right, my bad. Better use a clean Either here rather than subtypes. My example actually does not make sense (c could later also turn out to be a TupleType, which would mean we'd represent the wrong splice).
Basically what I found that in TypeScript:
- in functions: the rest argument
...rest: any[], can only be the last argument - in arrays: there can only exist 1 rest element
So, if we want to follow TypeScript, the SpliceType could be:
case class SpliceType(left: Ls[SimpleType], rep: Option[SimpleType], right: Ls[SimpleType])(val prov: TypeProvenance) extends BaseType
We can also continue with the original one and reject the ones that are ambiguous... I wonder what to do next @_@
1. in functions: the rest argument
...rest: any[], can only be the last argument
I don't think that's true. Here's an example valid JS program:
function myFunction(v, w, x, y, z) { }
let args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
The TS version needs some more type annotations, but also works:
function myFunction(v: number, w: number, x: number, y: number, z: number) { }
let args: [0, 1] = [0, 1];
myFunction(-1, ...args, 2, ...([3] as [3]));
2. in arrays: there can only exist 1 rest element
I don't think that's true either. Here's an example valid TS program:
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1];
Maybe here you're talking about patterns and parameter lists, which should indeed have such restrictions. We can add a check for this restriction later. In any case, the internal type representations should assume the general form.
Oh yes, I think I'm talking patterns when defining functions and type annotations 😂 Because we cannot do the following:
let ys: [number, number, ...number[], string, ...boolean[]]
function fx(a: string, ...b: string[], c: number) {}
function fy(a: string, ...b: string[], ...c: number[]) {}
So only patterns have restrictions, but spreading lists is not restricted...