clay
clay copied to clipboard
RFC: Replace tuples with record literals
record Tuple[..T] ( ..x:T );
[..T]
overload Tuple(..x:T) = Tuple[..T](..x);
foo( x:Tuple[..T] ) {...} // Matches named record
bar( x:[..T] ) {...} // Matches anonymous record
var myrec = Tuple(3, 5);
var myrec2 = [3, 5]; //This is an anonymous record with single variadic field
foo(myrec); // valid
foo(myrec2); // invalid
bar(myrec); // invalid
bar(myrec2); // valid
// Use keyword arguments to construct named fields
var a = [3, 5]; // Allowed -> unnamedRecord[..T](..data:T);
var b = [x = 3, y = 5]; // Allowed -> unnamedRecord(x:Int, y:Int);
var c = [x = 3, 5]; // Allowed -> unnamedRecord[..T](x:Int, ..data:T);
var d = [3, x = 5, 6] // Not allowed as cannot split variadic field
goo( rec:[x:Int, ..T] ) { } // Unification matches field names so ...
goo(c); // valid
goo(b); // invalid
Could also allow anonymous record type specs to match named records if fields match e.g. from the above example bar(myrec)
would be valid.
Sounds good to me; I always thought having variadic fields in records would be preferable to having builtin Tuple[..T] as a workaround. Keyword arguments in tuple/record constructors would be great too. One potential wrench in the works is that tuples are used for a bunch of builtin features, like recordWithProperties and function types. With keyword arguments, you could potentially support multiple variadic arguments to a function:
foo(..x, ..y) { ... }
bar() { foo(x = (1,2,3), y = (4, 5, 6)); }
Function type inputs and outputs could then be handled as keywords without tuples, such as CodePointer[inputs=(A, B), outputs=C]
, although that's getting pretty verbose.
Also, on a related note, how about adopting type definitions similar to those in Koka?
type MyType (); // Empty record
type Foo ( [a:Int32, b:MyType] ); // Effectively a record (brackets could be dropped here ...)
type Maybe [T] ( Nothing, T ); // Effectively a variant with Nothing as empty type
type Bar [T, ..U] (
Foo,
MyType,
Cabbage(x:Maybe[T], ..y:U), // New record-like type defined as part of variant-like type
);
A few constraints would be needed on the use of anonymous record literals etc.
(Hmm, probably should have put this one in #401)
Looks cool for me. Functional ADT syntax is totally reinvented. Comma in variants could be "|" to make it more consistent with constructor.
type MyType (); // constructed by MyType();
type Foo (a:Int32, b:MyType); //constructed by Foo(a, b);
type Maybe [T] ( Nothing | T ); //constucted by Maybe[Int](5);
Round brackets can be removed at all, but I don't like this because we have brackets in function calls. f(1,2)
, not f 1,2
ergo F[T](t:T)
. Otherwise haskell will be reinvented.
I meant just remove the square brackets for that special case, not all parentheses.
Remove square brackets? What is the difference between record and variant in this case?
I guess the idea is to unify type declarations so record and variant (and enum?) would just be special cases of type
.
Checkout Koka types.
The idea is clear, this unification is done in almost every language with algebraic datatypes, not only Koka. I asked about another thing. Koka uses the rule "record fields are separated by comma, variant instances are separated by space". I suggest the rule with comma and pipe symbol. In your case I dont see the difference between Maybe (variant) and Foo (record) if brackets are removed. The both are comma-separated
I suppose the difference is the same as that for static and non-static function arguments except that they cannot be mixed. This works as records cannot have static fields.
e.g.
type Foo ( Bar ); // Variant type as Bar is record
type Foo [T] ( Bar(a:T) ); // Variant with Bar record as member
type Maybe[T] ( Nothing, a:T ); // Illegal as 'a:T' is field
Variant instances don't have names, record fiels have... This works but looks too cryptic to me