Record update

This commit is contained in:
Jared Ramirez 2025-11-06 11:35:54 -05:00
parent b1b70e113b
commit 402922cd19
No known key found for this signature in database
GPG key ID: 41158983F521D68C
38 changed files with 468 additions and 270 deletions

View file

@ -2357,15 +2357,28 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected)
std.mem.sort(types_mod.RecordField, record_fields_scratch, self.cir.getIdentStore(), comptime types_mod.RecordField.sortByNameAsc); std.mem.sort(types_mod.RecordField, record_fields_scratch, self.cir.getIdentStore(), comptime types_mod.RecordField.sortByNameAsc);
const record_fields_range = try self.types.appendRecordFields(record_fields_scratch); const record_fields_range = try self.types.appendRecordFields(record_fields_scratch);
// Check if this is a record update
if (e.ext) |record_being_updated_expr| {
// Create an unbound record with the provided fields
const ext_var = try self.fresh(env, expr_region);
try self.unifyWith(expr_var, .{ .structure = .{ try self.unifyWith(expr_var, .{ .structure = .{
.record_unbound = record_fields_range, .record = .{
.fields = record_fields_range,
.ext = ext_var,
},
} }, env); } }, env);
// Process the ext if it exists does_fx = try self.checkExpr(record_being_updated_expr, env, .no_expectation) or does_fx;
if (e.ext) |ext_expr| { const record_being_updated_var = ModuleEnv.varFrom(record_being_updated_expr);
does_fx = try self.checkExpr(ext_expr, env, .no_expectation) or does_fx;
const ext_var = ModuleEnv.varFrom(ext_expr); _ = try self.unify(record_being_updated_var, expr_var, env);
_ = try self.unify(expr_var, ext_var, env); } else {
// Create an unbound record with the provided fields
const ext_var = try self.freshFromContent(.{ .structure = .empty_record }, env, expr_region);
try self.unifyWith(expr_var, .{ .structure = .{ .record = .{
.fields = record_fields_range,
.ext = ext_var,
} } }, env);
} }
}, },
.e_empty_record => { .e_empty_record => {

View file

@ -592,6 +592,7 @@ pub const SnapshotWriter = struct {
flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange), flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange),
flex_var_names: std.array_list.Managed(u8), flex_var_names: std.array_list.Managed(u8),
static_dispatch_constraints: std.array_list.Managed(SnapshotStaticDispatchConstraint), static_dispatch_constraints: std.array_list.Managed(SnapshotStaticDispatchConstraint),
scratch_record_fields: std.array_list.Managed(SnapshotRecordField),
count_seen_idxs: std.array_list.Managed(SnapshotContentIdx), count_seen_idxs: std.array_list.Managed(SnapshotContentIdx),
const FlexVarNameRange = struct { start: usize, end: usize }; const FlexVarNameRange = struct { start: usize, end: usize };
@ -610,6 +611,7 @@ pub const SnapshotWriter = struct {
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa), .flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
.flex_var_names = std.array_list.Managed(u8).init(gpa), .flex_var_names = std.array_list.Managed(u8).init(gpa),
.static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa), .static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa),
.scratch_record_fields = std.array_list.Managed(SnapshotRecordField).init(gpa),
.count_seen_idxs = std.array_list.Managed(SnapshotContentIdx).init(gpa), .count_seen_idxs = std.array_list.Managed(SnapshotContentIdx).init(gpa),
}; };
} }
@ -634,6 +636,7 @@ pub const SnapshotWriter = struct {
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa), .flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
.flex_var_names = std.array_list.Managed(u8).init(gpa), .flex_var_names = std.array_list.Managed(u8).init(gpa),
.static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa), .static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa),
.scratch_record_fields = std.array_list.Managed(SnapshotRecordField).init(gpa),
.count_seen_idxs = try std.array_list.Managed(SnapshotContentIdx).init(gpa), .count_seen_idxs = try std.array_list.Managed(SnapshotContentIdx).init(gpa),
}; };
} }
@ -643,6 +646,7 @@ pub const SnapshotWriter = struct {
self.flex_var_names_map.deinit(); self.flex_var_names_map.deinit();
self.flex_var_names.deinit(); self.flex_var_names.deinit();
self.static_dispatch_constraints.deinit(); self.static_dispatch_constraints.deinit();
self.scratch_record_fields.deinit();
self.count_seen_idxs.deinit(); self.count_seen_idxs.deinit();
} }
@ -653,6 +657,7 @@ pub const SnapshotWriter = struct {
self.flex_var_names_map.clearRetainingCapacity(); self.flex_var_names_map.clearRetainingCapacity();
self.flex_var_names.clearRetainingCapacity(); self.flex_var_names.clearRetainingCapacity();
self.static_dispatch_constraints.clearRetainingCapacity(); self.static_dispatch_constraints.clearRetainingCapacity();
self.scratch_record_fields.clearRetainingCapacity();
self.count_seen_idxs.clearRetainingCapacity(); self.count_seen_idxs.clearRetainingCapacity();
} }
@ -1013,43 +1018,111 @@ pub const SnapshotWriter = struct {
/// Write a record type /// Write a record type
fn writeRecord(self: *Self, record: SnapshotRecord, root_idx: SnapshotContentIdx) Allocator.Error!void { fn writeRecord(self: *Self, record: SnapshotRecord, root_idx: SnapshotContentIdx) Allocator.Error!void {
const ext = try self.gatherRecordFields(record.fields, record.ext);
const gathered_fields = self.scratch_record_fields.items;
const num_fields = gathered_fields.len;
// TODO: Sort gathered fields alphabetically
_ = try self.buf.writer().write("{ "); _ = try self.buf.writer().write("{ ");
const fields_slice = self.snapshots.record_fields.sliceRange(record.fields); switch (ext) {
.flex => |flex| {
if (fields_slice.len > 0) { if (flex.payload.name) |ident_idx| {
// Write first field _ = try self.buf.writer().write("..");
_ = try self.buf.writer().write(self.idents.getText(fields_slice.items(.name)[0])); _ = try self.buf.writer().write(self.idents.getText(ident_idx));
_ = try self.buf.writer().write(": "); if (num_fields > 0) _ = try self.buf.writer().write(", ");
try self.writeWithContext(fields_slice.items(.content)[0], .RecordFieldContent, root_idx); } else if (flex.payload.constraints.len() > 0 or try self.countOccurrences(flex.payload.var_, root_idx) > 1) {
_ = try self.buf.writer().write("..");
// Write remaining fields try self.writeFlexVarName(flex.payload.var_, flex.idx, .RecordExtension, root_idx);
for (fields_slice.items(.name)[1..], fields_slice.items(.content)[1..]) |name, content| { if (num_fields > 0) _ = try self.buf.writer().write(", ");
_ = try self.buf.writer().write(", ");
_ = try self.buf.writer().write(self.idents.getText(name));
_ = try self.buf.writer().write(": ");
try self.writeWithContext(content, .RecordFieldContent, root_idx);
}
} }
// Show extension variable if it's not empty // Since don't recurse above, we must capture the static dispatch
switch (self.snapshots.contents.get(record.ext).*) { // constraints directly
.structure => |flat_type| switch (flat_type) { for (self.snapshots.sliceStaticDispatchConstraints(flex.payload.constraints)) |constraint| {
.empty_record => {}, // Don't show empty extension try self.appendStaticDispatchConstraint(constraint);
else => { }
if (fields_slice.len > 0) _ = try self.buf.writer().write(", ");
try self.writeWithContext(record.ext, .RecordExtension, root_idx);
}, },
.rigid => |rigid| {
_ = try self.buf.writer().write("..");
_ = try self.buf.writer().write(self.idents.getText(rigid.name));
if (num_fields > 0) _ = try self.buf.writer().write(", ");
// Since don't recurse above, we must capture the static dispatch
// constraints directly
for (self.snapshots.sliceStaticDispatchConstraints(rigid.constraints)) |constraint| {
try self.appendStaticDispatchConstraint(constraint);
}
}, },
else => { .unbound, .invalid => {},
if (fields_slice.len > 0) _ = try self.buf.writer().write(", "); }
try self.writeWithContext(record.ext, .RecordExtension, root_idx);
}, for (gathered_fields, 0..) |field, i| {
_ = try self.buf.writer().write(self.idents.getText(field.name));
_ = try self.buf.writer().write(": ");
try self.writeWithContext(field.content, .RecordFieldContent, root_idx);
if (i != gathered_fields.len - 1) _ = try self.buf.writer().write(", ");
} }
_ = try self.buf.writer().write(" }"); _ = try self.buf.writer().write(" }");
} }
/// Recursively unwrap all record fields
fn gatherRecordFields(self: *Self, fields: SnapshotRecordFieldSafeList.Range, ext_var: SnapshotContentIdx) std.mem.Allocator.Error!union(enum) {
flex: struct { idx: SnapshotContentIdx, payload: SnapshotFlex },
rigid: SnapshotRigid,
unbound,
invalid,
} {
self.scratch_record_fields.clearRetainingCapacity();
const slice = self.snapshots.sliceRecordFields(fields);
try self.scratch_record_fields.ensureUnusedCapacity(fields.len());
for (slice.items(.name), slice.items(.content)) |name, content| {
self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .content = content });
}
var ext = ext_var;
while (true) {
const resolved = self.snapshots.contents.get(ext);
switch (resolved.*) {
.flex => |flex| {
return .{ .flex = .{ .idx = ext, .payload = flex } };
},
.rigid => |rigid| {
return .{ .rigid = rigid };
},
.alias => |alias| {
ext = alias.backing;
},
.structure => |flat_type| {
switch (flat_type) {
.record => |ext_record| {
const ext_slice = self.snapshots.sliceRecordFields(ext_record.fields);
try self.scratch_record_fields.ensureUnusedCapacity(ext_record.fields.len());
for (ext_slice.items(.name), ext_slice.items(.content)) |name, content| {
self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .content = content });
}
ext = ext_record.ext;
},
.record_unbound => |ext_fields| {
const ext_slice = self.snapshots.sliceRecordFields(ext_fields);
try self.scratch_record_fields.ensureUnusedCapacity(ext_fields.len());
for (ext_slice.items(.name), ext_slice.items(.content)) |name, content| {
self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .content = content });
}
return .unbound;
},
else => return .invalid,
}
},
else => return .invalid,
}
}
}
/// Write record fields without extension /// Write record fields without extension
fn writeRecordFields(self: *Self, fields: SnapshotRecordFieldSafeList.Range, root_idx: SnapshotContentIdx) Allocator.Error!void { fn writeRecordFields(self: *Self, fields: SnapshotRecordFieldSafeList.Range, root_idx: SnapshotContentIdx) Allocator.Error!void {
if (fields.isEmpty()) { if (fields.isEmpty()) {

View file

@ -426,10 +426,8 @@ pub fn assertDefType(self: *TestEnv, target_def_name: []const u8, expected: []co
.assign => |assign| { .assign => |assign| {
const def_name = idents.getText(assign.ident); const def_name = idents.getText(assign.ident);
if (std.mem.eql(u8, target_def_name, def_name)) { if (std.mem.eql(u8, target_def_name, def_name)) {
try testing.expectEqualStrings( try self.type_writer.write(ModuleEnv.varFrom(def_idx));
expected, try testing.expectEqualStrings(expected, self.type_writer.get());
try self.type_writer.writeGet(ModuleEnv.varFrom(def_idx)),
);
return; return;
} }
}, },
@ -453,8 +451,10 @@ pub fn assertLastDefType(self: *TestEnv, expected: []const u8) !void {
try testing.expect(self.module_env.all_defs.span.len > 0); try testing.expect(self.module_env.all_defs.span.len > 0);
const defs_slice = self.module_env.store.sliceDefs(self.module_env.all_defs); const defs_slice = self.module_env.store.sliceDefs(self.module_env.all_defs);
const last_def_idx = defs_slice[defs_slice.len - 1]; const last_def_idx = defs_slice[defs_slice.len - 1];
const last_def_var = ModuleEnv.varFrom(last_def_idx);
try testing.expectEqualStrings(expected, try self.type_writer.writeGet(ModuleEnv.varFrom(last_def_idx))); try self.type_writer.write(last_def_var);
try testing.expectEqualStrings(expected, self.type_writer.get());
} }
/// Get the inferred type descriptor of the last declaration /// Get the inferred type descriptor of the last declaration

View file

@ -113,7 +113,7 @@ test "polymorphic record constructor" {
\\{ \\{
\\ make_pair = |x, y| { first: x, second: y } \\ make_pair = |x, y| { first: x, second: y }
\\ pair1 = make_pair(1, "a") \\ pair1 = make_pair(1, "a")
\\ pair2 = make_pair("hello", 42) \\ pair2 = make_pair(1512351, 42)
\\ pair3 = make_pair(True, False) \\ pair3 = make_pair(True, False)
\\ { pair1, pair2, pair3 } \\ { pair1, pair2, pair3 }
\\} \\}

View file

@ -159,7 +159,7 @@ test "check type - tag" {
const source = const source =
\\MyTag \\MyTag
; ;
try checkTypesExpr(source, .pass, "[MyTag]_others"); try checkTypesExpr(source, .pass, "[MyTag]");
} }
test "check type - tag - args" { test "check type - tag - args" {
@ -804,27 +804,44 @@ test "check type - record - access - not a record" {
// record update // record update
test "check type - record - update" { test "check type - record - update 1" {
const source = const source =
\\update_data = |container, new_value| { ..container, data: new_value } \\update_data = |container, new_value| { ..container, data: new_value }
; ;
try checkTypesModule(source, .{ .pass = .{ .def = "update_data" } }, "a, b -> { data: b, ..a }"); try checkTypesModule(
source,
.{ .pass = .{ .def = "update_data" } },
"{ ..a, data: b }, b -> { ..a, data: b }",
);
} }
test "check type - record - update 2" { test "check type - record - update 2" {
const source = const source =
\\update_data = |container, new_value| { ..container, data: new_value } \\set_data = |container, new_value| { ..container, data: new_value }
\\ \\
\\updated1 = update_data({ data: 10 }, 100) \\updated1 = set_data({ data: 10 }, 100) # Updates field
\\updated2 = update_data({ data: 10, other: "hello" }, 100) \\updated2 = set_data({ data: 10, other: "hello" }, 100) # Updates with extra fields
\\updated3 = update_data({ data: "hello", other: 20 }, "world") \\updated3 = set_data({ data: "hello" }, "world") # Polymorphic
\\ \\
\\final = (updated1, updated2, updated3) \\final = (updated1, updated2, updated3)
; ;
try checkTypesModule( try checkTypesModule(
source, source,
.{ .pass = .{ .def = "final" } }, .{ .pass = .{ .def = "final" } },
"({ data: Num(_size) }, { data: Num(_size2), other: Str }, { data: Str, other: Num(_size3) })", "({ data: Num(_size) }, { data: Num(_size2), other: Str }, { data: Str })",
);
}
test "check type - record - update fail" {
const source =
\\set_data = |container, new_value| { ..container, data: new_value }
\\
\\updated = set_data({ data: "hello" }, 10)
;
try checkTypesModule(
source,
.fail,
"TYPE MISMATCH",
); );
} }

View file

@ -2540,6 +2540,9 @@ const Unifier = struct {
record_fields, record_fields,
) catch return Error.AllocatorError; ) catch return Error.AllocatorError;
// TODO: Below if we run into a record with the same field, then we
// prefer the leftmost field. But we should probably unify the dups
// then recursiv // then recursiv
var ext = record_ext; var ext = record_ext;
while (true) { while (true) {
@ -2561,20 +2564,43 @@ const Unifier = struct {
.structure => |flat_type| { .structure => |flat_type| {
switch (flat_type) { switch (flat_type) {
.record => |ext_record| { .record => |ext_record| {
const next_range = self.scratch.copyGatherFieldsFromMultiList( const next_fields = self.types_store.record_fields.sliceRange(ext_record.fields);
&self.types_store.record_fields, const already_gathered = self.scratch.gathered_fields.sliceRange(range);
ext_record.fields,
for (next_fields.items(.name), next_fields.items(.var_)) |name, var_| {
// Check if field name already exists (O(n) but fields are small)
const already_exists = for (already_gathered) |existing| {
if (existing.name == name) break true;
} else false;
// Only append if NOT already present (Map.union left-bias)
if (!already_exists) {
_ = self.scratch.gathered_fields.append(
self.scratch.gpa,
RecordField{ .name = name, .var_ = var_ },
) catch return Error.AllocatorError; ) catch return Error.AllocatorError;
range.count += next_range.count; range.count += 1;
}
}
ext = .{ .ext = ext_record.ext }; ext = .{ .ext = ext_record.ext };
}, },
.record_unbound => |fields| { .record_unbound => |fields| {
const next_range = self.scratch.copyGatherFieldsFromMultiList( const next_fields = self.types_store.record_fields.sliceRange(fields);
&self.types_store.record_fields, const already_gathered = self.scratch.gathered_fields.sliceRange(range);
fields,
for (next_fields.items(.name), next_fields.items(.var_)) |name, var_| {
const already_exists = for (already_gathered) |existing| {
if (existing.name == name) break true;
} else false;
if (!already_exists) {
_ = self.scratch.gathered_fields.append(
self.scratch.gpa,
RecordField{ .name = name, .var_ = var_ },
) catch return Error.AllocatorError; ) catch return Error.AllocatorError;
range.count += next_range.count; range.count += 1;
// record_unbound has no extension, so we're done }
}
return .{ .ext = ext, .range = range }; return .{ .ext = ext, .range = range };
}, },
.empty_record => { .empty_record => {

View file

@ -62,6 +62,7 @@ name_counters: std.EnumMap(TypeContext, u32),
flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange), flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange),
flex_var_names: std.array_list.Managed(u8), flex_var_names: std.array_list.Managed(u8),
static_dispatch_constraints: std.array_list.Managed(types_mod.StaticDispatchConstraint), static_dispatch_constraints: std.array_list.Managed(types_mod.StaticDispatchConstraint),
scratch_record_fields: std.array_list.Managed(types_mod.RecordField),
/// Mapping from fully-qualified type identifiers to their display names based on top-level imports. /// Mapping from fully-qualified type identifiers to their display names based on top-level imports.
/// This allows error messages to show "Str" instead of "Builtin.Str" for auto-imported types, /// This allows error messages to show "Str" instead of "Builtin.Str" for auto-imported types,
/// "Bar" instead of "Foo.Bar" for nested imports, and aliases like "Baz" instead of "Foo". /// "Bar" instead of "Foo.Bar" for nested imports, and aliases like "Baz" instead of "Foo".
@ -88,6 +89,7 @@ pub fn initFromParts(gpa: std.mem.Allocator, types_store: *const TypesStore, ide
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa), .flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
.flex_var_names = try std.array_list.Managed(u8).initCapacity(gpa, 32), .flex_var_names = try std.array_list.Managed(u8).initCapacity(gpa, 32),
.static_dispatch_constraints = try std.array_list.Managed(types_mod.StaticDispatchConstraint).initCapacity(gpa, 32), .static_dispatch_constraints = try std.array_list.Managed(types_mod.StaticDispatchConstraint).initCapacity(gpa, 32),
.scratch_record_fields = try std.array_list.Managed(types_mod.RecordField).initCapacity(gpa, 32),
.import_mapping = import_mapping, .import_mapping = import_mapping,
}; };
} }
@ -100,6 +102,7 @@ pub fn deinit(self: *TypeWriter) void {
self.flex_var_names_map.deinit(); self.flex_var_names_map.deinit();
self.flex_var_names.deinit(); self.flex_var_names.deinit();
self.static_dispatch_constraints.deinit(); self.static_dispatch_constraints.deinit();
self.scratch_record_fields.deinit();
self.import_mapping.deinit(); self.import_mapping.deinit();
} }
@ -111,6 +114,7 @@ pub fn reset(self: *TypeWriter) void {
self.flex_var_names_map.clearRetainingCapacity(); self.flex_var_names_map.clearRetainingCapacity();
self.flex_var_names.clearRetainingCapacity(); self.flex_var_names.clearRetainingCapacity();
self.static_dispatch_constraints.clearRetainingCapacity(); self.static_dispatch_constraints.clearRetainingCapacity();
self.scratch_record_fields.clearRetainingCapacity();
self.next_name_index = 0; self.next_name_index = 0;
self.name_counters = std.EnumMap(TypeContext, u32).init(.{}); self.name_counters = std.EnumMap(TypeContext, u32).init(.{});
@ -507,6 +511,8 @@ fn writeNominalType(self: *TypeWriter, nominal_type: NominalType, root_var: Var)
/// Write record fields without extension /// Write record fields without extension
fn writeRecordFields(self: *TypeWriter, fields: RecordField.SafeMultiList.Range, root_var: Var) std.mem.Allocator.Error!void { fn writeRecordFields(self: *TypeWriter, fields: RecordField.SafeMultiList.Range, root_var: Var) std.mem.Allocator.Error!void {
std.debug.print("\n----- writeRecordFields -----\n", .{});
if (fields.isEmpty()) { if (fields.isEmpty()) {
_ = try self.buf.writer().write("{}"); _ = try self.buf.writer().write("{}");
return; return;
@ -555,68 +561,109 @@ fn writeFuncWithArrow(self: *TypeWriter, func: Func, arrow: []const u8, root_var
/// Write a record type /// Write a record type
fn writeRecord(self: *TypeWriter, record: Record, root_var: Var) std.mem.Allocator.Error!void { fn writeRecord(self: *TypeWriter, record: Record, root_var: Var) std.mem.Allocator.Error!void {
const fields = self.types.getRecordFieldsSlice(record.fields); const ext = try self.gatherRecordFields(record.fields, record.ext);
const gathered_fields = self.scratch_record_fields.items;
const num_fields = gathered_fields.len;
// TODO: Sort gathered fields alphabetically
_ = try self.buf.writer().write("{ "); _ = try self.buf.writer().write("{ ");
for (fields.items(.name), fields.items(.var_), 0..) |field_name, field_var, i| {
if (i > 0) _ = try self.buf.writer().write(", ");
_ = try self.buf.writer().write(self.getIdent(field_name));
_ = try self.buf.writer().write(": ");
try self.writeVarWithContext(field_var, .RecordFieldContent, root_var);
}
try self.writeRecordExtension(record.ext, fields.len, root_var); switch (ext) {
_ = try self.buf.writer().write(" }");
}
/// Helper to write record extension, handling nested records
fn writeRecordExtension(self: *TypeWriter, ext_var: Var, num_fields: usize, root_var: Var) std.mem.Allocator.Error!void {
const ext_resolved = self.types.resolveVar(ext_var);
switch (ext_resolved.desc.content) {
.structure => |flat_type| switch (flat_type) {
.empty_record => {}, // Don't show empty extension
.record => |ext_record| {
// Flatten nested record extensions
const ext_fields = self.types.getRecordFieldsSlice(ext_record.fields);
for (ext_fields.items(.name), ext_fields.items(.var_)) |field_name, field_var| {
_ = try self.buf.writer().write(", ");
_ = try self.buf.writer().write(self.getIdent(field_name));
_ = try self.buf.writer().write(": ");
try self.writeVarWithContext(field_var, .RecordFieldContent, root_var);
}
// Recursively handle the extension's extension
try self.writeRecordExtension(ext_record.ext, num_fields + ext_fields.len, root_var);
},
else => {
if (num_fields > 0) _ = try self.buf.writer().write(", ");
try self.writeVarWithContext(ext_var, .RecordExtension, root_var);
},
},
.flex => |flex| { .flex => |flex| {
if (num_fields > 0) _ = try self.buf.writer().write(", "); if (flex.payload.name) |ident_idx| {
_ = try self.buf.writer().write(".."); _ = try self.buf.writer().write("..");
try self.writeVarWithContext(ext_var, .RecordExtension, root_var); _ = try self.buf.writer().write(self.getIdent(ident_idx));
if (num_fields > 0) _ = try self.buf.writer().write(", ");
} else if (flex.payload.constraints.len() > 0 or try self.countVarOccurrences(flex.var_, root_var) > 1) {
_ = try self.buf.writer().write("..");
try self.writeFlexVarName(flex.var_, .RecordExtension, root_var);
if (num_fields > 0) _ = try self.buf.writer().write(", ");
}
for (self.types.sliceStaticDispatchConstraints(flex.constraints)) |constraint| { // Since don't recurse above, we must capture the static dispatch
// constraints directly
for (self.types.sliceStaticDispatchConstraints(flex.payload.constraints)) |constraint| {
try self.appendStaticDispatchConstraint(constraint); try self.appendStaticDispatchConstraint(constraint);
} }
}, },
.rigid => |rigid| { .rigid => |rigid| {
// Show rigid vars with .. syntax
if (num_fields > 0) _ = try self.buf.writer().write(", ");
_ = try self.buf.writer().write(".."); _ = try self.buf.writer().write("..");
_ = try self.buf.writer().write(self.getIdent(rigid.name)); _ = try self.buf.writer().write(self.getIdent(rigid.name));
if (num_fields > 0) _ = try self.buf.writer().write(", ");
// Since don't recurse above, we must capture the static dispatch
// constraints directly
for (self.types.sliceStaticDispatchConstraints(rigid.constraints)) |constraint| { for (self.types.sliceStaticDispatchConstraints(rigid.constraints)) |constraint| {
try self.appendStaticDispatchConstraint(constraint); try self.appendStaticDispatchConstraint(constraint);
} }
}, },
else => { .unbound, .invalid => {},
// Show other types (aliases, errors, etc) }
if (num_fields > 0) _ = try self.buf.writer().write(", ");
try self.writeVarWithContext(ext_var, .RecordExtension, root_var); for (gathered_fields, 0..) |field, i| {
_ = try self.buf.writer().write(self.getIdent(field.name));
_ = try self.buf.writer().write(": ");
try self.writeVarWithContext(field.var_, .RecordFieldContent, root_var);
if (i != gathered_fields.len - 1) _ = try self.buf.writer().write(", ");
}
_ = try self.buf.writer().write(" }");
}
/// Recursively unwrap all record fields
fn gatherRecordFields(self: *TypeWriter, fields: RecordField.SafeMultiList.Range, ext_var: Var) std.mem.Allocator.Error!union(enum) {
flex: struct { var_: Var, payload: types_mod.Flex },
rigid: types_mod.Rigid,
unbound,
invalid,
} {
self.scratch_record_fields.clearRetainingCapacity();
const slice = self.types.getRecordFieldsSlice(fields);
try self.scratch_record_fields.ensureUnusedCapacity(fields.len());
for (slice.items(.name), slice.items(.var_)) |name, var_| {
self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .var_ = var_ });
}
var ext = ext_var;
while (true) {
const resolved = self.types.resolveVar(ext);
switch (resolved.desc.content) {
.flex => |flex| {
return .{ .flex = .{ .var_ = resolved.var_, .payload = flex } };
}, },
.rigid => |rigid| {
return .{ .rigid = rigid };
},
.alias => |alias| {
ext = self.types.getAliasBackingVar(alias);
},
.structure => |flat_type| {
switch (flat_type) {
.record => |ext_record| {
const ext_slice = self.types.getRecordFieldsSlice(ext_record.fields);
try self.scratch_record_fields.ensureUnusedCapacity(ext_record.fields.len());
for (ext_slice.items(.name), ext_slice.items(.var_)) |name, var_| {
self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .var_ = var_ });
}
ext = ext_record.ext;
},
.record_unbound => |ext_fields| {
const ext_slice = self.types.getRecordFieldsSlice(ext_fields);
try self.scratch_record_fields.ensureUnusedCapacity(ext_fields.len());
for (ext_slice.items(.name), ext_slice.items(.var_)) |name, var_| {
self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .var_ = var_ });
}
return .unbound;
},
else => return .invalid,
}
},
else => return .invalid,
}
} }
} }
@ -652,7 +699,7 @@ fn writeTagUnion(self: *TypeWriter, tag_union: TagUnion, root_var: Var) std.mem.
.flex => |flex| { .flex => |flex| {
if (flex.name) |ident_idx| { if (flex.name) |ident_idx| {
_ = try self.buf.writer().write(self.getIdent(ident_idx)); _ = try self.buf.writer().write(self.getIdent(ident_idx));
} else { } else if (flex.constraints.len() > 0 or try self.countVarOccurrences(ext_resolved.var_, root_var) > 1) {
try self.writeFlexVarName(tag_union.ext, .TagUnionExtension, root_var); try self.writeFlexVarName(tag_union.ext, .TagUnionExtension, root_var);
} }

View file

@ -46,6 +46,8 @@ pub const ResolvedVarDescs = store.ResolvedVarDescs;
pub const Store = store.Store; pub const Store = store.Store;
pub const DescStoreIdx = store.DescStoreIdx; pub const DescStoreIdx = store.DescStoreIdx;
pub const Polarity = types.Polarity;
test { test {
std.testing.refAllDecls(@import("test/test_rigid_instantiation.zig")); std.testing.refAllDecls(@import("test/test_rigid_instantiation.zig"));
} }

View file

@ -223,6 +223,21 @@ pub const Content = union(enum) {
else => return null, else => return null,
} }
} }
/// Unwrap a function (pure, eff, or unbound) and return it
pub fn unwrapFuncFull(content: Self) ?struct { func: Func, ext: enum { unbound, pure, effectful } } {
switch (content) {
.structure => |flat_type| {
switch (flat_type) {
.fn_pure => |func| return .{ .func = func, .ext = .pure },
.fn_effectful => |func| return .{ .func = func, .ext = .effectful },
.fn_unbound => |func| return .{ .func = func, .ext = .unbound },
else => return null,
}
},
else => return null,
}
}
}; };
// flex // // flex //
@ -994,3 +1009,14 @@ pub const TwoStaticDispatchConstraints = struct {
/// A safe multi list of tag union fields /// A safe multi list of tag union fields
pub const SafeMultiList = MkSafeMultiList(@This()); pub const SafeMultiList = MkSafeMultiList(@This());
}; };
/// Polarity of a type, or roughly, what side of an arrow it appears on.
pub const Polarity = enum {
/// A type that appears in negative/input position
neg,
/// A type that appears in positive/output position
pos,
pub const lhs = Polarity.neg;
pub const rhs = Polarity.pos;
};

View file

@ -1224,7 +1224,7 @@ main = {
(patt (type "(a -> a), a -> a")) (patt (type "(a -> a), a -> a"))
(patt (type "(a -> b) -> ((b -> c) -> (a -> c))")) (patt (type "(a -> b) -> ((b -> c) -> (a -> c))"))
(patt (type "a, c -> d where [a.map : a, (b -> c) -> d]")) (patt (type "a, c -> d where [a.map : a, (b -> c) -> d]"))
(patt (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))) (patt (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")))
(type_decls (type_decls
(nominal (type "Container(a)") (nominal (type "Container(a)")
(ty-header (name "Container") (ty-header (name "Container")
@ -1238,5 +1238,5 @@ main = {
(expr (type "(a -> a), a -> a")) (expr (type "(a -> a), a -> a"))
(expr (type "(a -> b) -> ((b -> c) -> (a -> c))")) (expr (type "(a -> b) -> ((b -> c) -> (a -> c))"))
(expr (type "a, c -> d where [a.map : a, (b -> c) -> d]")) (expr (type "a, c -> d where [a.map : a, (b -> c) -> d]"))
(expr (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")))) (expr (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))))
~~~ ~~~

View file

@ -67,5 +67,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "([True]_others, [False]_others2, Bool, Bool, Bool, Bool, Bool, Bool)")) (expr (type "([True], [False], Bool, Bool, Bool, Bool, Bool, Bool)"))
~~~ ~~~

View file

@ -35,5 +35,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "([True]_others, [False]_others2)")) (expr (type "([True], [False])"))
~~~ ~~~

View file

@ -70,8 +70,8 @@ NO CHANGE
(inferred-types (inferred-types
(defs (defs
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }"))) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")))
(expressions (expressions
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")))) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))))
~~~ ~~~

View file

@ -120,12 +120,12 @@ NO CHANGE
(inferred-types (inferred-types
(defs (defs
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }")) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }"))
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str, .._others }")) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }"))
(patt (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")) (patt (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str, .._others }"))) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str }")))
(expressions (expressions
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }")) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }"))
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str, .._others }")) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }"))
(expr (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")) (expr (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str, .._others }")))) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str }"))))
~~~ ~~~

View file

@ -30,5 +30,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "[MyTag]_others")) (expr (type "[MyTag]"))
~~~ ~~~

View file

@ -164,5 +164,5 @@ EndOfFile,
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "{ addOne: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], errTag: [Err(Str)]_others, nested: [Some([Ok([Just(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others2)]_others3)]_others4, noneTag: [None]_others5, okTag: [Ok(Str)]_others6, result: Error, someTag: [Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others7, tagList: List([Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])][None]_others8) }")) (expr (type "{ addOne: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], errTag: [Err(Str)], nested: [Some([Ok([Just(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])])])], noneTag: [None], okTag: [Ok(Str)], result: Error, someTag: [Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])], tagList: List([Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])][None]) }"))
~~~ ~~~

View file

@ -34,5 +34,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "[Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others")) (expr (type "[Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]"))
~~~ ~~~

View file

@ -39,5 +39,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others)")) (expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True])"))
~~~ ~~~

View file

@ -39,5 +39,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others)")) (expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True])"))
~~~ ~~~

View file

@ -2001,9 +2001,9 @@ expect {
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "Error")) (patt (type "Error"))
(patt (type "Bool -> Error")) (patt (type "Bool -> Error"))
(patt (type "[Blue]_others, [Tb]_others2 -> Error")) (patt (type "[Blue], [Tb] -> Error"))
(patt (type "Error")) (patt (type "Error"))
(patt (type "_arg -> [Stdo!(Error)]_others")) (patt (type "_arg -> [Stdo!(Error)]"))
(patt (type "{ }")) (patt (type "{ }"))
(patt (type "{}")) (patt (type "{}"))
(patt (type "Error"))) (patt (type "Error")))
@ -2038,9 +2038,9 @@ expect {
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "Error")) (expr (type "Error"))
(expr (type "Bool -> Error")) (expr (type "Bool -> Error"))
(expr (type "[Blue]_others, [Tb]_others2 -> Error")) (expr (type "[Blue], [Tb] -> Error"))
(expr (type "Error")) (expr (type "Error"))
(expr (type "_arg -> [Stdo!(Error)]_others")) (expr (type "_arg -> [Stdo!(Error)]"))
(expr (type "{ }")) (expr (type "{ }"))
(expr (type "{}")) (expr (type "{}"))
(expr (type "Error")))) (expr (type "Error"))))

View file

@ -1979,10 +1979,10 @@ expect {
(patt (type "()")) (patt (type "()"))
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "Error")) (patt (type "Error"))
(patt (type "[Rum]_others -> Error")) (patt (type "[Rum] -> Error"))
(patt (type "[Blue]_others -> Error")) (patt (type "[Blue] -> Error"))
(patt (type "Error")) (patt (type "Error"))
(patt (type "_arg -> [Stdo!(Error)]_others")) (patt (type "_arg -> [Stdo!(Error)]"))
(patt (type "{ }")) (patt (type "{ }"))
(patt (type "{}")) (patt (type "{}"))
(patt (type "Error"))) (patt (type "Error")))
@ -2016,10 +2016,10 @@ expect {
(expr (type "()")) (expr (type "()"))
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "Error")) (expr (type "Error"))
(expr (type "[Rum]_others -> Error")) (expr (type "[Rum] -> Error"))
(expr (type "[Blue]_others -> Error")) (expr (type "[Blue] -> Error"))
(expr (type "Error")) (expr (type "Error"))
(expr (type "_arg -> [Stdo!(Error)]_others")) (expr (type "_arg -> [Stdo!(Error)]"))
(expr (type "{ }")) (expr (type "{ }"))
(expr (type "{}")) (expr (type "{}"))
(expr (type "Error")))) (expr (type "Error"))))

View file

@ -2574,7 +2574,7 @@ expect {
(defs (defs
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "Error -> Num(Int(Unsigned64))")) (patt (type "Error -> Num(Int(Unsigned64))"))
(patt (type "[Red][Blue, Green]_others, _arg -> Error")) (patt (type "[Red][Blue, Green], _arg -> Error"))
(patt (type "Error")) (patt (type "Error"))
(patt (type "List(Error) -> Error")) (patt (type "List(Error) -> Error"))
(patt (type "{}")) (patt (type "{}"))
@ -2621,7 +2621,7 @@ expect {
(expressions (expressions
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "Error -> Num(Int(Unsigned64))")) (expr (type "Error -> Num(Int(Unsigned64))"))
(expr (type "[Red][Blue, Green]_others, _arg -> Error")) (expr (type "[Red][Blue, Green], _arg -> Error"))
(expr (type "Error")) (expr (type "Error"))
(expr (type "List(Error) -> Error")) (expr (type "List(Error) -> Error"))
(expr (type "{}")) (expr (type "{}"))

View file

@ -2252,7 +2252,7 @@ expect {
(patt (type "(Error, Error)")) (patt (type "(Error, Error)"))
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "Error -> Num(Int(Unsigned64))")) (patt (type "Error -> Num(Int(Unsigned64))"))
(patt (type "[Red, Blue]_others, _arg -> Error")) (patt (type "[Red, Blue], _arg -> Error"))
(patt (type "List(Error) -> Error")) (patt (type "List(Error) -> Error"))
(patt (type "{}")) (patt (type "{}"))
(patt (type "Error"))) (patt (type "Error")))
@ -2289,7 +2289,7 @@ expect {
(expr (type "(Error, Error)")) (expr (type "(Error, Error)"))
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "Error -> Num(Int(Unsigned64))")) (expr (type "Error -> Num(Int(Unsigned64))"))
(expr (type "[Red, Blue]_others, _arg -> Error")) (expr (type "[Red, Blue], _arg -> Error"))
(expr (type "List(Error) -> Error")) (expr (type "List(Error) -> Error"))
(expr (type "{}")) (expr (type "{}"))
(expr (type "Error")))) (expr (type "Error"))))

View file

@ -76,8 +76,8 @@ updated = {
(inferred-types (inferred-types
(defs (defs
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }"))) (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")))
(expressions (expressions
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")))) (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))))
~~~ ~~~

View file

@ -56,5 +56,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "[A, B]_others")) (expr (type "[A, B]"))
~~~ ~~~

View file

@ -49,5 +49,5 @@ NO CHANGE
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "[Ok(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), Err(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others")) (expr (type "[Ok(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), Err(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]"))
~~~ ~~~

View file

@ -1058,58 +1058,58 @@ main = |_| {
(patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) (patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])"))
(patt (type "Str")) (patt (type "Str"))
(patt (type "[True]_others")) (patt (type "[True]"))
(patt (type "List(_elem)")) (patt (type "List(_elem)"))
(patt (type "{}")) (patt (type "{}"))
(patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])"))
(patt (type "List(Str)")) (patt (type "List(Str)"))
(patt (type "List([True, False]_others)")) (patt (type "List([True, False])"))
(patt (type "List(List(_elem))")) (patt (type "List(List(_elem))"))
(patt (type "List(List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]))")) (patt (type "List(List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]))"))
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(_elem) }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(_elem) }"))
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(Str) }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(Str) }"))
(patt (type "{ data: List(_elem), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } }")) (patt (type "{ data: List(_elem), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } }"))
(patt (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) (patt (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) (patt (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "a -> { value: a, wrapper: List(a) }")) (patt (type "a -> { value: a, wrapper: List(a) }"))
(patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], wrapper: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], wrapper: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
(patt (type "{ value: Str, wrapper: List(Str) }")) (patt (type "{ value: Str, wrapper: List(Str) }"))
(patt (type "{ value: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), wrapper: List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])) }")) (patt (type "{ value: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), wrapper: List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])) }"))
(patt (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, results: List({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), tag: Str, .._others }) }")) (patt (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])"))
(patt (type "{ base: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], derived: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (patt (type "{ base: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], derived: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
(patt (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, empty_lists: { in_list: List(List(_elem)), in_record: { data: List(_elem2) }, raw: List(_elem3) }, numbers: { float: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), list: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, strings: { list: List(Str), value: Str } }")) (patt (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), strings: { list: List(Str), value: Str } }"))
(patt (type "_arg -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))) (patt (type "_arg -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))
(expressions (expressions
(expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) (expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])"))
(expr (type "Str")) (expr (type "Str"))
(expr (type "[True]_others")) (expr (type "[True]"))
(expr (type "List(_elem)")) (expr (type "List(_elem)"))
(expr (type "{}")) (expr (type "{}"))
(expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])"))
(expr (type "List(Str)")) (expr (type "List(Str)"))
(expr (type "List([True, False]_others)")) (expr (type "List([True, False])"))
(expr (type "List(List(_elem))")) (expr (type "List(List(_elem))"))
(expr (type "List(List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]))")) (expr (type "List(List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]))"))
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(_elem) }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(_elem) }"))
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(Str) }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(Str) }"))
(expr (type "{ data: List(_elem), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } }")) (expr (type "{ data: List(_elem), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } }"))
(expr (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) (expr (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) (expr (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "a -> { value: a, wrapper: List(a) }")) (expr (type "a -> { value: a, wrapper: List(a) }"))
(expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], wrapper: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], wrapper: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
(expr (type "{ value: Str, wrapper: List(Str) }")) (expr (type "{ value: Str, wrapper: List(Str) }"))
(expr (type "{ value: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), wrapper: List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])) }")) (expr (type "{ value: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), wrapper: List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])) }"))
(expr (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, results: List({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), tag: Str, .._others }) }")) (expr (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])"))
(expr (type "{ base: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], derived: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (expr (type "{ base: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], derived: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
(expr (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, empty_lists: { in_list: List(List(_elem)), in_record: { data: List(_elem2) }, raw: List(_elem3) }, numbers: { float: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), list: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, strings: { list: List(Str), value: Str } }")) (expr (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), strings: { list: List(Str), value: Str } }"))
(expr (type "_arg -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))) (expr (type "_arg -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))))
~~~ ~~~

View file

@ -22,14 +22,15 @@ int_container = make_container(num)
str_container = make_container(str) str_container = make_container(str)
list_container = make_container(my_empty_list) list_container = make_container(my_empty_list)
# TODO
# Polymorphic record update # Polymorphic record update
update_data : a, b -> { data: b, ..a } # TODO: 11/06/2025 currently record ext syntax is not parsable
# update_data : { ..a, data: b }, b -> { ..a, data: b }
update_data = |container, new_value| { ..container, data: new_value } update_data = |container, new_value| { ..container, data: new_value }
# Used with different record types # Used with different record types
# updated_int = update_data(int_container, 100) updated_int = update_data(int_container, 100)
# updated_str = update_data(str_container, "world") updated_str = update_data(str_container, "world")
updated_mismatch = update_data(str_container, 99)
# Function returning polymorphic record # Function returning polymorphic record
identity_record = |x| { value: x } identity_record = |x| { value: x }
@ -40,62 +41,44 @@ str_record = identity_record("test")
list_record = identity_record([1, 2, 3]) list_record = identity_record([1, 2, 3])
main = |_| { main = |_| {
# Mismatch to snapshot infererd type of update_data
1 + update_data
# Access polymorphic fields # Access polymorphic fields
int_container.count + str_container.count int_container.count + str_container.count
} }
~~~ ~~~
# EXPECTED # EXPECTED
UNEXPECTED TOKEN IN EXPRESSION - let_polymorphism_records.md:20:50:20:51 TYPE MISMATCH - let_polymorphism_records.md:26:47:26:49
UNRECOGNIZED SYNTAX - let_polymorphism_records.md:20:50:20:51 TYPE MISMATCH - let_polymorphism_records.md:38:6:38:17
UNUSED VARIABLE - let_polymorphism_records.md:20:52:20:67
UNUSED VARIABLE - let_polymorphism_records.md:20:27:20:36
UNUSED VALUE - let_polymorphism_records.md:20:40:20:49
# PROBLEMS # PROBLEMS
**UNEXPECTED TOKEN IN TYPE ANNOTATION** **TYPE MISMATCH**
The token **..** is not expected in a type annotation. The second argument being passed to this function has the wrong type:
Type annotations should contain types like _Str_, _Num a_, or _List U64_. **let_polymorphism_records.md:26:47:26:49:**
**let_polymorphism_records.md:20:34:20:36:**
```roc ```roc
update_data : a, b -> { data: b, ..a } updated_mismatch = update_data(str_container, 99)
``` ```
^^ ^^
This argument has the type:
_Num(_size)_
**PARSE ERROR** But `update_data` needs the second argument to be:
A parsing error occurred: `expected_arrow` _Str_
This is an unexpected parsing error. Please check your syntax.
**let_polymorphism_records.md:20:31:20:32:**
```roc
update_data : a, b -> { data: b, ..a }
```
^
**MALFORMED TYPE**
This type annotation is malformed or contains invalid syntax.
**let_polymorphism_records.md:20:31:20:37:**
```roc
update_data : a, b -> { data: b, ..a }
```
^^^^^^
**TYPE MISMATCH** **TYPE MISMATCH**
This expression is used in an unexpected way: This expression is used in an unexpected way:
**let_polymorphism_records.md:21:42:21:51:** **let_polymorphism_records.md:38:6:38:17:**
```roc ```roc
update_data = |container, new_value| { ..container, data: new_value } 1 + update_data
``` ```
^^^^^^^^^ ^^^^^^^^^^^
It has the type: It has the type:
_a_ _{ ..a, data: b }, b -> { ..a, data: b }_
But I expected it to be: But I expected it to be:
_{ data: b }_ _Num(_size)_
# TOKENS # TOKENS
~~~zig ~~~zig
@ -109,13 +92,16 @@ LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIde
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,
LowerIdent,OpColon,LowerIdent,Comma,LowerIdent,OpArrow,OpenCurly,LowerIdent,OpColon,LowerIdent,Comma,DoubleDot,LowerIdent,CloseCurly,
LowerIdent,OpAssign,OpBar,LowerIdent,Comma,LowerIdent,OpBar,OpenCurly,DoubleDot,LowerIdent,Comma,LowerIdent,OpColon,LowerIdent,CloseCurly, LowerIdent,OpAssign,OpBar,LowerIdent,Comma,LowerIdent,OpBar,OpenCurly,DoubleDot,LowerIdent,Comma,LowerIdent,OpColon,LowerIdent,CloseCurly,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,Int,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,StringStart,StringPart,StringEnd,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,Int,CloseRound,
LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIdent,CloseCurly, LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIdent,CloseCurly,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,Int,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,Int,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,OpenSquare,Int,Comma,Int,Comma,Int,CloseSquare,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,OpenSquare,Int,Comma,Int,Comma,Int,CloseSquare,CloseRound,
LowerIdent,OpAssign,OpBar,Underscore,OpBar,OpenCurly, LowerIdent,OpAssign,OpBar,Underscore,OpBar,OpenCurly,
Int,OpPlus,LowerIdent,
LowerIdent,NoSpaceDotLowerIdent,OpPlus,LowerIdent,NoSpaceDotLowerIdent, LowerIdent,NoSpaceDotLowerIdent,OpPlus,LowerIdent,NoSpaceDotLowerIdent,
CloseCurly, CloseCurly,
EndOfFile, EndOfFile,
@ -178,13 +164,6 @@ EndOfFile,
(e-apply (e-apply
(e-ident (raw "make_container")) (e-ident (raw "make_container"))
(e-ident (raw "my_empty_list")))) (e-ident (raw "my_empty_list"))))
(s-type-anno (name "update_data")
(ty-fn
(ty-var (raw "a"))
(ty-var (raw "b"))
(ty-record
(anno-record-field (name "data")
(ty-malformed (tag "expected_arrow"))))))
(s-decl (s-decl
(p-ident (raw "update_data")) (p-ident (raw "update_data"))
(e-lambda (e-lambda
@ -196,6 +175,25 @@ EndOfFile,
(e-ident (raw "container"))) (e-ident (raw "container")))
(field (field "data") (field (field "data")
(e-ident (raw "new_value")))))) (e-ident (raw "new_value"))))))
(s-decl
(p-ident (raw "updated_int"))
(e-apply
(e-ident (raw "update_data"))
(e-ident (raw "int_container"))
(e-int (raw "100"))))
(s-decl
(p-ident (raw "updated_str"))
(e-apply
(e-ident (raw "update_data"))
(e-ident (raw "str_container"))
(e-string
(e-string-part (raw "world")))))
(s-decl
(p-ident (raw "updated_mismatch"))
(e-apply
(e-ident (raw "update_data"))
(e-ident (raw "str_container"))
(e-int (raw "99"))))
(s-decl (s-decl
(p-ident (raw "identity_record")) (p-ident (raw "identity_record"))
(e-lambda (e-lambda
@ -230,6 +228,9 @@ EndOfFile,
(p-underscore)) (p-underscore))
(e-block (e-block
(statements (statements
(e-binop (op "+")
(e-int (raw "1"))
(e-ident (raw "update_data")))
(e-binop (op "+") (e-binop (op "+")
(e-field-access (e-field-access
(e-ident (raw "int_container")) (e-ident (raw "int_container"))
@ -240,44 +241,7 @@ EndOfFile,
~~~ ~~~
# FORMATTED # FORMATTED
~~~roc ~~~roc
app [main] { pf: platform "../basic-cli/platform.roc" } NO CHANGE
# Basic values for polymorphism testing
num = 42
frac = 4.2
str = "hello"
my_empty_list = []
my_nonempty_list = [num, frac]
# Record with polymorphic field
make_container = |value| { data: value, count: 1 }
# Used with different types
int_container = make_container(num)
str_container = make_container(str)
list_container = make_container(my_empty_list)
# TODO
# Polymorphic record update
update_data : a, b -> { data : }
update_data = |container, new_value| { ..container, data: new_value }
# Used with different record types
# updated_int = update_data(int_container, 100)
# updated_str = update_data(str_container, "world")
# Function returning polymorphic record
identity_record = |x| { value: x }
# Used at different types
int_record = identity_record(42)
str_record = identity_record("test")
list_record = identity_record([1, 2, 3])
main = |_| {
# Access polymorphic fields
int_container.count + str_container.count
}
~~~ ~~~
# CANONICALIZE # CANONICALIZE
~~~clojure ~~~clojure
@ -349,14 +313,32 @@ main = |_| {
(fields (fields
(field (name "data") (field (name "data")
(e-lookup-local (e-lookup-local
(p-assign (ident "new_value"))))))) (p-assign (ident "new_value"))))))))
(annotation (d-let
(ty-fn (effectful false) (p-assign (ident "updated_int"))
(ty-rigid-var (name "a")) (e-call
(ty-rigid-var (name "b")) (e-lookup-local
(ty-record (p-assign (ident "update_data")))
(field (field "data") (e-lookup-local
(ty-malformed)))))) (p-assign (ident "int_container")))
(e-num (value "100"))))
(d-let
(p-assign (ident "updated_str"))
(e-call
(e-lookup-local
(p-assign (ident "update_data")))
(e-lookup-local
(p-assign (ident "str_container")))
(e-string
(e-literal (string "world")))))
(d-let
(p-assign (ident "updated_mismatch"))
(e-call
(e-lookup-local
(p-assign (ident "update_data")))
(e-lookup-local
(p-assign (ident "str_container")))
(e-num (value "99"))))
(d-let (d-let
(p-assign (ident "identity_record")) (p-assign (ident "identity_record"))
(e-lambda (e-lambda
@ -394,12 +376,18 @@ main = |_| {
(p-assign (ident "main")) (p-assign (ident "main"))
(e-closure (e-closure
(captures (captures
(capture (ident "update_data"))
(capture (ident "int_container")) (capture (ident "int_container"))
(capture (ident "str_container"))) (capture (ident "str_container")))
(e-lambda (e-lambda
(args (args
(p-underscore)) (p-underscore))
(e-block (e-block
(s-expr
(e-binop (op "add")
(e-num (value "1"))
(e-lookup-local
(p-assign (ident "update_data")))))
(e-binop (op "add") (e-binop (op "add")
(e-dot-access (field "count") (e-dot-access (field "count")
(receiver (receiver
@ -419,12 +407,15 @@ main = |_| {
(patt (type "Str")) (patt (type "Str"))
(patt (type "List(_elem)")) (patt (type "List(_elem)"))
(patt (type "List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]))")) (patt (type "List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]))"))
(patt (type "c -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: c }")) (patt (type "a -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: a }"))
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }"))
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }"))
(patt (type "Error, b -> Error")) (patt (type "{ ..a, data: b }, b -> { ..a, data: b }"))
(patt (type "c -> { value: c }")) (patt (type "{ data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "{ data: Str, count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "Error"))
(patt (type "a -> { value: a }"))
(patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(patt (type "{ value: Str }")) (patt (type "{ value: Str }"))
(patt (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (patt (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
@ -435,12 +426,15 @@ main = |_| {
(expr (type "Str")) (expr (type "Str"))
(expr (type "List(_elem)")) (expr (type "List(_elem)"))
(expr (type "List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]))")) (expr (type "List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]))"))
(expr (type "c -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: c }")) (expr (type "a -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: a }"))
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }"))
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }"))
(expr (type "Error, b -> Error")) (expr (type "{ ..a, data: b }, b -> { ..a, data: b }"))
(expr (type "c -> { value: c }")) (expr (type "{ data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "{ data: Str, count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "Error"))
(expr (type "a -> { value: a }"))
(expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
(expr (type "{ value: Str }")) (expr (type "{ value: Str }"))
(expr (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (expr (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))

View file

@ -88,5 +88,5 @@ match l {
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "[Err([EmptyList]_others), Ok(_a)]_others2")) (expr (type "[Err([EmptyList]), Ok(_a)]"))
~~~ ~~~

View file

@ -110,5 +110,5 @@ match l {
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "[Err([EmptyList]_others), Ok(_a)]_others2")) (expr (type "[Err([EmptyList]), Ok(_a)]"))
~~~ ~~~

View file

@ -40,7 +40,7 @@ NO CHANGE
~~~clojure ~~~clojure
(inferred-types (inferred-types
(defs (defs
(patt (type "[FortyTwo]_others"))) (patt (type "[FortyTwo]")))
(expressions (expressions
(expr (type "[FortyTwo]_others")))) (expr (type "[FortyTwo]"))))
~~~ ~~~

View file

@ -137,5 +137,5 @@ EndOfFile,
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "{ address: { city: Str, coordinates: { lat: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), lng: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]) }, street: Str }, contact: { email: Str, phone: { home: Str, work: Str } }, person: { age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str } }")) (expr (type "{ address: { city: Str, coordinates: { lat: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), lng: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]) }, street: Str }, lng: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), street: Str }"))
~~~ ~~~

View file

@ -234,5 +234,5 @@ EndOfFile,
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure
(expr (type "{ callback: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], metadata: [Ok({ permissions: List([Read, Write, Admin]_others), tags: List(Str) })]_others2, name: Str, nested: { items: List([Some(Str)][None]_others3), result: [Success({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), timestamp: Str })]_others4 }, preferences: { notifications: [Email(Str)]_others5, theme: [Dark]_others6 }, scores: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), status: [Active({ since: Str })]_others7 }")) (expr (type "{ callback: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], metadata: [Ok({ permissions: List([Read, Write, Admin]), tags: List(Str) })], name: Str, nested: { items: List([Some(Str)][None]), result: [Success({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), timestamp: Str })] }, preferences: { notifications: [Email(Str)], theme: [Dark] }, scores: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), status: [Active({ since: Str })] }"))
~~~ ~~~

View file

@ -434,7 +434,7 @@ func = {
(inferred-types (inferred-types
(defs (defs
(patt (type "Container(a), (a -> b) -> Container(b)")) (patt (type "Container(a), (a -> b) -> Container(b)"))
(patt (type "[Value(c), Empty]_others, c -> c")) (patt (type "[Value(c), Empty], c -> c"))
(patt (type "Container(a), (a -> Container(b)) -> Container(b)")) (patt (type "Container(a), (a -> Container(b)) -> Container(b)"))
(patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))
(type_decls (type_decls
@ -444,7 +444,7 @@ func = {
(ty-rigid-var (name "a")))))) (ty-rigid-var (name "a"))))))
(expressions (expressions
(expr (type "Container(a), (a -> b) -> Container(b)")) (expr (type "Container(a), (a -> b) -> Container(b)"))
(expr (type "[Value(c), Empty]_others, c -> c")) (expr (type "[Value(c), Empty], c -> c"))
(expr (type "Container(a), (a -> Container(b)) -> Container(b)")) (expr (type "Container(a), (a -> Container(b)) -> Container(b)"))
(expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))))
~~~ ~~~

View file

@ -2460,7 +2460,7 @@ expect {
(defs (defs
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(patt (type "Error -> Num(Int(Unsigned64))")) (patt (type "Error -> Num(Int(Unsigned64))"))
(patt (type "[Red][Blue, Green]_others, _arg -> Error")) (patt (type "[Red][Blue, Green], _arg -> Error"))
(patt (type "List(Error) -> Error")) (patt (type "List(Error) -> Error"))
(patt (type "{}")) (patt (type "{}"))
(patt (type "Error"))) (patt (type "Error")))
@ -2506,7 +2506,7 @@ expect {
(expressions (expressions
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
(expr (type "Error -> Num(Int(Unsigned64))")) (expr (type "Error -> Num(Int(Unsigned64))"))
(expr (type "[Red][Blue, Green]_others, _arg -> Error")) (expr (type "[Red][Blue, Green], _arg -> Error"))
(expr (type "List(Error) -> Error")) (expr (type "List(Error) -> Error"))
(expr (type "{}")) (expr (type "{}"))
(expr (type "Error")))) (expr (type "Error"))))

View file

@ -588,8 +588,8 @@ main! = |_| {
(patt (type "ac -> ac")) (patt (type "ac -> ac"))
(patt (type "Str")) (patt (type "Str"))
(patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) (patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])"))
(patt (type "[True]_others")) (patt (type "[True]"))
(patt (type "[False]_others")) (patt (type "[False]"))
(patt (type "ac -> ac")) (patt (type "ac -> ac"))
(patt (type "ac, ad -> (ac, ad)")) (patt (type "ac, ad -> (ac, ad)"))
(patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
@ -623,8 +623,8 @@ main! = |_| {
(expr (type "ac -> ac")) (expr (type "ac -> ac"))
(expr (type "Str")) (expr (type "Str"))
(expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) (expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])"))
(expr (type "[True]_others")) (expr (type "[True]"))
(expr (type "[False]_others")) (expr (type "[False]"))
(expr (type "ac -> ac")) (expr (type "ac -> ac"))
(expr (type "ac, ad -> (ac, ad)")) (expr (type "ac, ad -> (ac, ad)"))
(expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))