mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-22 16:18:39 +00:00
Record update
This commit is contained in:
parent
b1b70e113b
commit
402922cd19
38 changed files with 468 additions and 270 deletions
|
|
@ -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);
|
||||
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 = .{
|
||||
.record_unbound = record_fields_range,
|
||||
.record = .{
|
||||
.fields = record_fields_range,
|
||||
.ext = ext_var,
|
||||
},
|
||||
} }, env);
|
||||
|
||||
// Process the ext if it exists
|
||||
if (e.ext) |ext_expr| {
|
||||
does_fx = try self.checkExpr(ext_expr, env, .no_expectation) or does_fx;
|
||||
const ext_var = ModuleEnv.varFrom(ext_expr);
|
||||
_ = try self.unify(expr_var, ext_var, env);
|
||||
does_fx = try self.checkExpr(record_being_updated_expr, env, .no_expectation) or does_fx;
|
||||
const record_being_updated_var = ModuleEnv.varFrom(record_being_updated_expr);
|
||||
|
||||
_ = try self.unify(record_being_updated_var, expr_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 => {
|
||||
|
|
|
|||
|
|
@ -592,6 +592,7 @@ pub const SnapshotWriter = struct {
|
|||
flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange),
|
||||
flex_var_names: std.array_list.Managed(u8),
|
||||
static_dispatch_constraints: std.array_list.Managed(SnapshotStaticDispatchConstraint),
|
||||
scratch_record_fields: std.array_list.Managed(SnapshotRecordField),
|
||||
count_seen_idxs: std.array_list.Managed(SnapshotContentIdx),
|
||||
|
||||
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 = std.array_list.Managed(u8).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),
|
||||
};
|
||||
}
|
||||
|
|
@ -634,6 +636,7 @@ pub const SnapshotWriter = struct {
|
|||
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
|
||||
.flex_var_names = std.array_list.Managed(u8).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),
|
||||
};
|
||||
}
|
||||
|
|
@ -643,6 +646,7 @@ pub const SnapshotWriter = struct {
|
|||
self.flex_var_names_map.deinit();
|
||||
self.flex_var_names.deinit();
|
||||
self.static_dispatch_constraints.deinit();
|
||||
self.scratch_record_fields.deinit();
|
||||
self.count_seen_idxs.deinit();
|
||||
}
|
||||
|
||||
|
|
@ -653,6 +657,7 @@ pub const SnapshotWriter = struct {
|
|||
self.flex_var_names_map.clearRetainingCapacity();
|
||||
self.flex_var_names.clearRetainingCapacity();
|
||||
self.static_dispatch_constraints.clearRetainingCapacity();
|
||||
self.scratch_record_fields.clearRetainingCapacity();
|
||||
self.count_seen_idxs.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
|
|
@ -1013,43 +1018,111 @@ pub const SnapshotWriter = struct {
|
|||
|
||||
/// Write a record type
|
||||
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("{ ");
|
||||
|
||||
const fields_slice = self.snapshots.record_fields.sliceRange(record.fields);
|
||||
|
||||
if (fields_slice.len > 0) {
|
||||
// Write first field
|
||||
_ = try self.buf.writer().write(self.idents.getText(fields_slice.items(.name)[0]));
|
||||
_ = try self.buf.writer().write(": ");
|
||||
try self.writeWithContext(fields_slice.items(.content)[0], .RecordFieldContent, root_idx);
|
||||
|
||||
// Write remaining fields
|
||||
for (fields_slice.items(.name)[1..], fields_slice.items(.content)[1..]) |name, content| {
|
||||
_ = 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);
|
||||
}
|
||||
switch (ext) {
|
||||
.flex => |flex| {
|
||||
if (flex.payload.name) |ident_idx| {
|
||||
_ = try self.buf.writer().write("..");
|
||||
_ = try self.buf.writer().write(self.idents.getText(ident_idx));
|
||||
if (num_fields > 0) _ = try self.buf.writer().write(", ");
|
||||
} else if (flex.payload.constraints.len() > 0 or try self.countOccurrences(flex.payload.var_, root_idx) > 1) {
|
||||
_ = try self.buf.writer().write("..");
|
||||
try self.writeFlexVarName(flex.payload.var_, flex.idx, .RecordExtension, root_idx);
|
||||
if (num_fields > 0) _ = try self.buf.writer().write(", ");
|
||||
}
|
||||
|
||||
// Show extension variable if it's not empty
|
||||
switch (self.snapshots.contents.get(record.ext).*) {
|
||||
.structure => |flat_type| switch (flat_type) {
|
||||
.empty_record => {}, // Don't show empty extension
|
||||
else => {
|
||||
if (fields_slice.len > 0) _ = try self.buf.writer().write(", ");
|
||||
try self.writeWithContext(record.ext, .RecordExtension, root_idx);
|
||||
// Since don't recurse above, we must capture the static dispatch
|
||||
// constraints directly
|
||||
for (self.snapshots.sliceStaticDispatchConstraints(flex.payload.constraints)) |constraint| {
|
||||
try self.appendStaticDispatchConstraint(constraint);
|
||||
}
|
||||
},
|
||||
.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 => {
|
||||
if (fields_slice.len > 0) _ = try self.buf.writer().write(", ");
|
||||
try self.writeWithContext(record.ext, .RecordExtension, root_idx);
|
||||
},
|
||||
.unbound, .invalid => {},
|
||||
}
|
||||
|
||||
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(" }");
|
||||
}
|
||||
|
||||
/// 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
|
||||
fn writeRecordFields(self: *Self, fields: SnapshotRecordFieldSafeList.Range, root_idx: SnapshotContentIdx) Allocator.Error!void {
|
||||
if (fields.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -426,10 +426,8 @@ pub fn assertDefType(self: *TestEnv, target_def_name: []const u8, expected: []co
|
|||
.assign => |assign| {
|
||||
const def_name = idents.getText(assign.ident);
|
||||
if (std.mem.eql(u8, target_def_name, def_name)) {
|
||||
try testing.expectEqualStrings(
|
||||
expected,
|
||||
try self.type_writer.writeGet(ModuleEnv.varFrom(def_idx)),
|
||||
);
|
||||
try self.type_writer.write(ModuleEnv.varFrom(def_idx));
|
||||
try testing.expectEqualStrings(expected, self.type_writer.get());
|
||||
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);
|
||||
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_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
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ test "polymorphic record constructor" {
|
|||
\\{
|
||||
\\ make_pair = |x, y| { first: x, second: y }
|
||||
\\ pair1 = make_pair(1, "a")
|
||||
\\ pair2 = make_pair("hello", 42)
|
||||
\\ pair2 = make_pair(1512351, 42)
|
||||
\\ pair3 = make_pair(True, False)
|
||||
\\ { pair1, pair2, pair3 }
|
||||
\\}
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ test "check type - tag" {
|
|||
const source =
|
||||
\\MyTag
|
||||
;
|
||||
try checkTypesExpr(source, .pass, "[MyTag]_others");
|
||||
try checkTypesExpr(source, .pass, "[MyTag]");
|
||||
}
|
||||
|
||||
test "check type - tag - args" {
|
||||
|
|
@ -804,27 +804,44 @@ test "check type - record - access - not a record" {
|
|||
|
||||
// record update
|
||||
|
||||
test "check type - record - update" {
|
||||
test "check type - record - update 1" {
|
||||
const source =
|
||||
\\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" {
|
||||
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)
|
||||
\\updated2 = update_data({ data: 10, other: "hello" }, 100)
|
||||
\\updated3 = update_data({ data: "hello", other: 20 }, "world")
|
||||
\\updated1 = set_data({ data: 10 }, 100) # Updates field
|
||||
\\updated2 = set_data({ data: 10, other: "hello" }, 100) # Updates with extra fields
|
||||
\\updated3 = set_data({ data: "hello" }, "world") # Polymorphic
|
||||
\\
|
||||
\\final = (updated1, updated2, updated3)
|
||||
;
|
||||
try checkTypesModule(
|
||||
source,
|
||||
.{ .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",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2540,6 +2540,9 @@ const Unifier = struct {
|
|||
record_fields,
|
||||
) 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
|
||||
var ext = record_ext;
|
||||
while (true) {
|
||||
|
|
@ -2561,20 +2564,43 @@ const Unifier = struct {
|
|||
.structure => |flat_type| {
|
||||
switch (flat_type) {
|
||||
.record => |ext_record| {
|
||||
const next_range = self.scratch.copyGatherFieldsFromMultiList(
|
||||
&self.types_store.record_fields,
|
||||
ext_record.fields,
|
||||
const next_fields = self.types_store.record_fields.sliceRange(ext_record.fields);
|
||||
const already_gathered = self.scratch.gathered_fields.sliceRange(range);
|
||||
|
||||
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;
|
||||
range.count += next_range.count;
|
||||
range.count += 1;
|
||||
}
|
||||
}
|
||||
ext = .{ .ext = ext_record.ext };
|
||||
},
|
||||
.record_unbound => |fields| {
|
||||
const next_range = self.scratch.copyGatherFieldsFromMultiList(
|
||||
&self.types_store.record_fields,
|
||||
fields,
|
||||
const next_fields = self.types_store.record_fields.sliceRange(fields);
|
||||
const already_gathered = self.scratch.gathered_fields.sliceRange(range);
|
||||
|
||||
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;
|
||||
range.count += next_range.count;
|
||||
// record_unbound has no extension, so we're done
|
||||
range.count += 1;
|
||||
}
|
||||
}
|
||||
return .{ .ext = ext, .range = range };
|
||||
},
|
||||
.empty_record => {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ name_counters: std.EnumMap(TypeContext, u32),
|
|||
flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange),
|
||||
flex_var_names: std.array_list.Managed(u8),
|
||||
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.
|
||||
/// 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".
|
||||
|
|
@ -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 = try std.array_list.Managed(u8).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,
|
||||
};
|
||||
}
|
||||
|
|
@ -100,6 +102,7 @@ pub fn deinit(self: *TypeWriter) void {
|
|||
self.flex_var_names_map.deinit();
|
||||
self.flex_var_names.deinit();
|
||||
self.static_dispatch_constraints.deinit();
|
||||
self.scratch_record_fields.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.clearRetainingCapacity();
|
||||
self.static_dispatch_constraints.clearRetainingCapacity();
|
||||
self.scratch_record_fields.clearRetainingCapacity();
|
||||
|
||||
self.next_name_index = 0;
|
||||
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
|
||||
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()) {
|
||||
_ = try self.buf.writer().write("{}");
|
||||
return;
|
||||
|
|
@ -555,68 +561,109 @@ fn writeFuncWithArrow(self: *TypeWriter, func: Func, arrow: []const u8, root_var
|
|||
|
||||
/// Write a record type
|
||||
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("{ ");
|
||||
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);
|
||||
|
||||
_ = 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);
|
||||
},
|
||||
},
|
||||
switch (ext) {
|
||||
.flex => |flex| {
|
||||
if (num_fields > 0) _ = try self.buf.writer().write(", ");
|
||||
if (flex.payload.name) |ident_idx| {
|
||||
_ = 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);
|
||||
}
|
||||
},
|
||||
.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(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| {
|
||||
try self.appendStaticDispatchConstraint(constraint);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
// Show other types (aliases, errors, etc)
|
||||
if (num_fields > 0) _ = try self.buf.writer().write(", ");
|
||||
try self.writeVarWithContext(ext_var, .RecordExtension, root_var);
|
||||
.unbound, .invalid => {},
|
||||
}
|
||||
|
||||
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| {
|
||||
if (flex.name) |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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ pub const ResolvedVarDescs = store.ResolvedVarDescs;
|
|||
pub const Store = store.Store;
|
||||
pub const DescStoreIdx = store.DescStoreIdx;
|
||||
|
||||
pub const Polarity = types.Polarity;
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@import("test/test_rigid_instantiation.zig"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,6 +223,21 @@ pub const Content = union(enum) {
|
|||
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 //
|
||||
|
|
@ -994,3 +1009,14 @@ pub const TwoStaticDispatchConstraints = struct {
|
|||
/// A safe multi list of tag union fields
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1224,7 +1224,7 @@ main = {
|
|||
(patt (type "(a -> a), a -> a"))
|
||||
(patt (type "(a -> b) -> ((b -> c) -> (a -> c))"))
|
||||
(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
|
||||
(nominal (type "Container(a)")
|
||||
(ty-header (name "Container")
|
||||
|
|
@ -1238,5 +1238,5 @@ main = {
|
|||
(expr (type "(a -> a), a -> a"))
|
||||
(expr (type "(a -> b) -> ((b -> c) -> (a -> c))"))
|
||||
(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])] }"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -67,5 +67,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(expr (type "([True]_others, [False]_others2, Bool, Bool, Bool, Bool, Bool, Bool)"))
|
||||
(expr (type "([True], [False], Bool, Bool, Bool, Bool, Bool, Bool)"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -35,5 +35,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(expr (type "([True]_others, [False]_others2)"))
|
||||
(expr (type "([True], [False])"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ NO CHANGE
|
|||
(inferred-types
|
||||
(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, .._others }")))
|
||||
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")))
|
||||
(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, .._others }"))))
|
||||
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -120,12 +120,12 @@ NO CHANGE
|
|||
(inferred-types
|
||||
(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, .._others }"))
|
||||
(patt (type "{ city: Str, 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, city: 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 }"))
|
||||
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str }")))
|
||||
(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, .._others }"))
|
||||
(expr (type "{ city: Str, 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, city: 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 }"))
|
||||
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str }"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -30,5 +30,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(expr (type "[MyTag]_others"))
|
||||
(expr (type "[MyTag]"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -164,5 +164,5 @@ EndOfFile,
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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]) }"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -34,5 +34,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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])])]"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -39,5 +39,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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])"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -39,5 +39,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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])"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -2001,9 +2001,9 @@ expect {
|
|||
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "Bool -> Error"))
|
||||
(patt (type "[Blue]_others, [Tb]_others2 -> Error"))
|
||||
(patt (type "[Blue], [Tb] -> Error"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "_arg -> [Stdo!(Error)]_others"))
|
||||
(patt (type "_arg -> [Stdo!(Error)]"))
|
||||
(patt (type "{ }"))
|
||||
(patt (type "{}"))
|
||||
(patt (type "Error")))
|
||||
|
|
@ -2038,9 +2038,9 @@ expect {
|
|||
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "Bool -> Error"))
|
||||
(expr (type "[Blue]_others, [Tb]_others2 -> Error"))
|
||||
(expr (type "[Blue], [Tb] -> Error"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "_arg -> [Stdo!(Error)]_others"))
|
||||
(expr (type "_arg -> [Stdo!(Error)]"))
|
||||
(expr (type "{ }"))
|
||||
(expr (type "{}"))
|
||||
(expr (type "Error"))))
|
||||
|
|
|
|||
|
|
@ -1979,10 +1979,10 @@ expect {
|
|||
(patt (type "()"))
|
||||
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "[Rum]_others -> Error"))
|
||||
(patt (type "[Blue]_others -> Error"))
|
||||
(patt (type "[Rum] -> Error"))
|
||||
(patt (type "[Blue] -> Error"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "_arg -> [Stdo!(Error)]_others"))
|
||||
(patt (type "_arg -> [Stdo!(Error)]"))
|
||||
(patt (type "{ }"))
|
||||
(patt (type "{}"))
|
||||
(patt (type "Error")))
|
||||
|
|
@ -2016,10 +2016,10 @@ expect {
|
|||
(expr (type "()"))
|
||||
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "[Rum]_others -> Error"))
|
||||
(expr (type "[Blue]_others -> Error"))
|
||||
(expr (type "[Rum] -> Error"))
|
||||
(expr (type "[Blue] -> Error"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "_arg -> [Stdo!(Error)]_others"))
|
||||
(expr (type "_arg -> [Stdo!(Error)]"))
|
||||
(expr (type "{ }"))
|
||||
(expr (type "{}"))
|
||||
(expr (type "Error"))))
|
||||
|
|
|
|||
|
|
@ -2574,7 +2574,7 @@ expect {
|
|||
(defs
|
||||
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(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 "List(Error) -> Error"))
|
||||
(patt (type "{}"))
|
||||
|
|
@ -2621,7 +2621,7 @@ expect {
|
|||
(expressions
|
||||
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(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 "List(Error) -> Error"))
|
||||
(expr (type "{}"))
|
||||
|
|
|
|||
|
|
@ -2252,7 +2252,7 @@ expect {
|
|||
(patt (type "(Error, Error)"))
|
||||
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(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 "{}"))
|
||||
(patt (type "Error")))
|
||||
|
|
@ -2289,7 +2289,7 @@ expect {
|
|||
(expr (type "(Error, Error)"))
|
||||
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(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 "{}"))
|
||||
(expr (type "Error"))))
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -76,8 +76,8 @@ updated = {
|
|||
(inferred-types
|
||||
(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, .._others }")))
|
||||
(patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")))
|
||||
(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, .._others }"))))
|
||||
(expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))))
|
||||
~~~
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -56,5 +56,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(expr (type "[A, B]_others"))
|
||||
(expr (type "[A, B]"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ NO CHANGE
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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])])]"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -1058,58 +1058,58 @@ main = |_| {
|
|||
(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 "Str"))
|
||||
(patt (type "[True]_others"))
|
||||
(patt (type "[True]"))
|
||||
(patt (type "List(_elem)"))
|
||||
(patt (type "{}"))
|
||||
(patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])"))
|
||||
(patt (type "List(Str)"))
|
||||
(patt (type "List([True, False]_others)"))
|
||||
(patt (type "List([True, False])"))
|
||||
(patt (type "List(List(_elem))"))
|
||||
(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(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 "{ 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(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(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])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
|
||||
(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: 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 "{ 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 "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])]")))
|
||||
(expressions
|
||||
(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 "Str"))
|
||||
(expr (type "[True]_others"))
|
||||
(expr (type "[True]"))
|
||||
(expr (type "List(_elem)"))
|
||||
(expr (type "{}"))
|
||||
(expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])"))
|
||||
(expr (type "List(Str)"))
|
||||
(expr (type "List([True, False]_others)"))
|
||||
(expr (type "List([True, False])"))
|
||||
(expr (type "List(List(_elem))"))
|
||||
(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(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 "{ 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(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(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])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))
|
||||
(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: 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 "{ 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 "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])]"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -22,14 +22,15 @@ 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: 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 }
|
||||
|
||||
# Used with different record types
|
||||
# updated_int = update_data(int_container, 100)
|
||||
# updated_str = update_data(str_container, "world")
|
||||
updated_int = update_data(int_container, 100)
|
||||
updated_str = update_data(str_container, "world")
|
||||
updated_mismatch = update_data(str_container, 99)
|
||||
|
||||
# Function returning polymorphic record
|
||||
identity_record = |x| { value: x }
|
||||
|
|
@ -40,62 +41,44 @@ str_record = identity_record("test")
|
|||
list_record = identity_record([1, 2, 3])
|
||||
|
||||
main = |_| {
|
||||
# Mismatch to snapshot infererd type of update_data
|
||||
1 + update_data
|
||||
|
||||
# Access polymorphic fields
|
||||
int_container.count + str_container.count
|
||||
}
|
||||
~~~
|
||||
# EXPECTED
|
||||
UNEXPECTED TOKEN IN EXPRESSION - let_polymorphism_records.md:20:50:20:51
|
||||
UNRECOGNIZED SYNTAX - let_polymorphism_records.md:20:50:20:51
|
||||
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
|
||||
TYPE MISMATCH - let_polymorphism_records.md:26:47:26:49
|
||||
TYPE MISMATCH - let_polymorphism_records.md:38:6:38:17
|
||||
# PROBLEMS
|
||||
**UNEXPECTED TOKEN IN TYPE ANNOTATION**
|
||||
The token **..** is not expected in a type annotation.
|
||||
Type annotations should contain types like _Str_, _Num a_, or _List U64_.
|
||||
|
||||
**let_polymorphism_records.md:20:34:20:36:**
|
||||
**TYPE MISMATCH**
|
||||
The second argument being passed to this function has the wrong type:
|
||||
**let_polymorphism_records.md:26:47:26:49:**
|
||||
```roc
|
||||
update_data : a, b -> { data: b, ..a }
|
||||
updated_mismatch = update_data(str_container, 99)
|
||||
```
|
||||
^^
|
||||
|
||||
This argument has the type:
|
||||
_Num(_size)_
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `expected_arrow`
|
||||
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 }
|
||||
```
|
||||
^^^^^^
|
||||
|
||||
But `update_data` needs the second argument to be:
|
||||
_Str_
|
||||
|
||||
**TYPE MISMATCH**
|
||||
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
|
||||
update_data = |container, new_value| { ..container, data: new_value }
|
||||
1 + update_data
|
||||
```
|
||||
^^^^^^^^^
|
||||
^^^^^^^^^^^
|
||||
|
||||
It has the type:
|
||||
_a_
|
||||
_{ ..a, data: b }, b -> { ..a, data: b }_
|
||||
|
||||
But I expected it to be:
|
||||
_{ data: b }_
|
||||
_Num(_size)_
|
||||
|
||||
# TOKENS
|
||||
~~~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,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,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,LowerIdent,NoSpaceOpenRound,Int,CloseRound,
|
||||
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound,
|
||||
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,OpenSquare,Int,Comma,Int,Comma,Int,CloseSquare,CloseRound,
|
||||
LowerIdent,OpAssign,OpBar,Underscore,OpBar,OpenCurly,
|
||||
Int,OpPlus,LowerIdent,
|
||||
LowerIdent,NoSpaceDotLowerIdent,OpPlus,LowerIdent,NoSpaceDotLowerIdent,
|
||||
CloseCurly,
|
||||
EndOfFile,
|
||||
|
|
@ -178,13 +164,6 @@ EndOfFile,
|
|||
(e-apply
|
||||
(e-ident (raw "make_container"))
|
||||
(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
|
||||
(p-ident (raw "update_data"))
|
||||
(e-lambda
|
||||
|
|
@ -196,6 +175,25 @@ EndOfFile,
|
|||
(e-ident (raw "container")))
|
||||
(field (field "data")
|
||||
(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
|
||||
(p-ident (raw "identity_record"))
|
||||
(e-lambda
|
||||
|
|
@ -230,6 +228,9 @@ EndOfFile,
|
|||
(p-underscore))
|
||||
(e-block
|
||||
(statements
|
||||
(e-binop (op "+")
|
||||
(e-int (raw "1"))
|
||||
(e-ident (raw "update_data")))
|
||||
(e-binop (op "+")
|
||||
(e-field-access
|
||||
(e-ident (raw "int_container"))
|
||||
|
|
@ -240,44 +241,7 @@ EndOfFile,
|
|||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
app [main] { pf: platform "../basic-cli/platform.roc" }
|
||||
|
||||
# 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
|
||||
}
|
||||
NO CHANGE
|
||||
~~~
|
||||
# CANONICALIZE
|
||||
~~~clojure
|
||||
|
|
@ -349,14 +313,32 @@ main = |_| {
|
|||
(fields
|
||||
(field (name "data")
|
||||
(e-lookup-local
|
||||
(p-assign (ident "new_value")))))))
|
||||
(annotation
|
||||
(ty-fn (effectful false)
|
||||
(ty-rigid-var (name "a"))
|
||||
(ty-rigid-var (name "b"))
|
||||
(ty-record
|
||||
(field (field "data")
|
||||
(ty-malformed))))))
|
||||
(p-assign (ident "new_value"))))))))
|
||||
(d-let
|
||||
(p-assign (ident "updated_int"))
|
||||
(e-call
|
||||
(e-lookup-local
|
||||
(p-assign (ident "update_data")))
|
||||
(e-lookup-local
|
||||
(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
|
||||
(p-assign (ident "identity_record"))
|
||||
(e-lambda
|
||||
|
|
@ -394,12 +376,18 @@ main = |_| {
|
|||
(p-assign (ident "main"))
|
||||
(e-closure
|
||||
(captures
|
||||
(capture (ident "update_data"))
|
||||
(capture (ident "int_container"))
|
||||
(capture (ident "str_container")))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-underscore))
|
||||
(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-dot-access (field "count")
|
||||
(receiver
|
||||
|
|
@ -419,12 +407,15 @@ main = |_| {
|
|||
(patt (type "Str"))
|
||||
(patt (type "List(_elem)"))
|
||||
(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: Str }"))
|
||||
(patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }"))
|
||||
(patt (type "Error, b -> Error"))
|
||||
(patt (type "c -> { value: c }"))
|
||||
(patt (type "{ ..a, data: b }, b -> { ..a, data: b }"))
|
||||
(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: Str }"))
|
||||
(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 "List(_elem)"))
|
||||
(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: Str }"))
|
||||
(expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }"))
|
||||
(expr (type "Error, b -> Error"))
|
||||
(expr (type "c -> { value: c }"))
|
||||
(expr (type "{ ..a, data: b }, b -> { ..a, data: b }"))
|
||||
(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: Str }"))
|
||||
(expr (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }"))
|
||||
|
|
|
|||
|
|
@ -88,5 +88,5 @@ match l {
|
|||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(expr (type "[Err([EmptyList]_others), Ok(_a)]_others2"))
|
||||
(expr (type "[Err([EmptyList]), Ok(_a)]"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -110,5 +110,5 @@ match l {
|
|||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(expr (type "[Err([EmptyList]_others), Ok(_a)]_others2"))
|
||||
(expr (type "[Err([EmptyList]), Ok(_a)]"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ NO CHANGE
|
|||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "[FortyTwo]_others")))
|
||||
(patt (type "[FortyTwo]")))
|
||||
(expressions
|
||||
(expr (type "[FortyTwo]_others"))))
|
||||
(expr (type "[FortyTwo]"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -137,5 +137,5 @@ EndOfFile,
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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 }"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -234,5 +234,5 @@ EndOfFile,
|
|||
~~~
|
||||
# TYPES
|
||||
~~~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 })] }"))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ func = {
|
|||
(inferred-types
|
||||
(defs
|
||||
(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 "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))
|
||||
(type_decls
|
||||
|
|
@ -444,7 +444,7 @@ func = {
|
|||
(ty-rigid-var (name "a"))))))
|
||||
(expressions
|
||||
(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 "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -2460,7 +2460,7 @@ expect {
|
|||
(defs
|
||||
(patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(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 "{}"))
|
||||
(patt (type "Error")))
|
||||
|
|
@ -2506,7 +2506,7 @@ expect {
|
|||
(expressions
|
||||
(expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
(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 "{}"))
|
||||
(expr (type "Error"))))
|
||||
|
|
|
|||
|
|
@ -588,8 +588,8 @@ main! = |_| {
|
|||
(patt (type "ac -> ac"))
|
||||
(patt (type "Str"))
|
||||
(patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])"))
|
||||
(patt (type "[True]_others"))
|
||||
(patt (type "[False]_others"))
|
||||
(patt (type "[True]"))
|
||||
(patt (type "[False]"))
|
||||
(patt (type "ac -> ac"))
|
||||
(patt (type "ac, ad -> (ac, ad)"))
|
||||
(patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
|
|
@ -623,8 +623,8 @@ main! = |_| {
|
|||
(expr (type "ac -> ac"))
|
||||
(expr (type "Str"))
|
||||
(expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])"))
|
||||
(expr (type "[True]_others"))
|
||||
(expr (type "[False]_others"))
|
||||
(expr (type "[True]"))
|
||||
(expr (type "[False]"))
|
||||
(expr (type "ac -> ac"))
|
||||
(expr (type "ac, ad -> (ac, ad)"))
|
||||
(expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue