builtins work for List.last, any, all, contains, and working on append.

This commit is contained in:
Edwin Santos 2025-11-27 13:28:29 -05:00
parent 4b811d2dba
commit 8bfd2deee3
3 changed files with 198 additions and 0 deletions

View file

@ -48,6 +48,53 @@ Builtin :: [].{
$state
}
append : List(a), a -> List(a)
append = |list, elt| {
new_tail : List(a)
new_tail = [elt]
List.concat(list, new_tail)
}
# append_if_ok : List(a), Try(a, err) -> List(a)
# append_if_ok : |list, try| match try {
# Ok(elt) => List.append(list, elt)
# Err(_) => list
# }
last : List(a) -> Try(a, [ListWasEmpty])
last = |list| {
len = List.len(list)
if len == 0 {
Try.Err(ListWasEmpty)
} else {
Try.Ok(list_get_unsafe(list, len - 1))
}
}
any : List(a), (a -> Bool) -> Bool
# any = |list, predicate| {
# for item in list {
# if predicate(item) {
# return True
# }
# }
# False
# }
any = |list, fn| {
List.fold(list, False, |state, item| fn(item) or state)
}
all : List(a), (a -> Bool) -> Bool
all = |list, fn| {
List.fold(list, True, |state, item| fn(item) and state)
}
contains : List(a), a -> Bool where [a.is_eq: a, a -> Bool]
contains = |list, elt| {
List.any(list, |x| x == elt)
}
}
Bool := [False, True].{

View file

@ -957,6 +957,146 @@ test "interpreter: List.fold from Builtin using numbers" {
try std.testing.expectEqualStrings("6", rendered);
}
test "interpreter: List.last on Empty List" {
const roc_src = "List.last([])";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("Err(ListWasEmpty)", rendered);
}
test "interpreter: List.last on single element List" {
const roc_src = "List.last([-1])";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("Ok(-1)", rendered);
}
test "interpreter: List.last on multi-item List" {
const roc_src = "List.last([1, 2, 3, 4, 5, 6, 7, 8, 9])";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("Ok(9)", rendered);
}
test "interpreter: List.any True on integers" {
const roc_src = "List.any([1, 0, 1, 0, -1], |x| x > 0)";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("True", rendered);
}
test "interpreter: List.any False on unsigned integers" {
const roc_src = "List.any([9, 8, 7, 6, 5], |x| x > 10)";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("False", rendered);
}
test "interpreter: List.all False when some elements are False" {
const roc_src = "List.all([9, 18, 7, 6, 15], |x| x < 10)";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("False", rendered);
}
test "interpreter: List.all True on small integers" {
const roc_src = "List.all([9, 8, 7, 6, 5], |x| x < 10)";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);
defer helpers.cleanupParseAndCanonical(std.testing.allocator, resources);
const imported_envs = [_]*const can.ModuleEnv{resources.builtin_module.env};
var interp2 = try Interpreter.init(std.testing.allocator, resources.module_env, resources.builtin_types, resources.builtin_module.env, &imported_envs, &resources.checker.import_mapping);
defer interp2.deinit();
var host = TestHost.init(std.testing.allocator);
defer host.deinit();
var ops = host.makeOps();
const result = try interp2.evalMinimal(resources.expr_idx, &ops);
const rt_var = try interp2.translateTypeVar(resources.module_env, can.ModuleEnv.varFrom(resources.expr_idx));
const rendered = try interp2.renderValueRocWithType(result, rt_var);
defer std.testing.allocator.free(rendered);
try std.testing.expectEqualStrings("True", rendered);
}
test "interpreter: crash statement triggers crash error and message" {
const roc_src = "{\n crash \"boom\"\n 0\n}";
const resources = try helpers.parseAndCanonicalizeExpr(std.testing.allocator, roc_src);

View file

@ -636,6 +636,17 @@ test "e_low_level_lambda - List.concat preserves order" {
try testing.expectEqualStrings("Ok(10)", first_value);
}
// test "e_low_level_lambda - List.append on empty" {
// const src =
// \\x = List.append([9], 11)
// \\last = List.get(x, 2)
// ;
//
// const first_value = try evalModuleAndGetString(src, 1, test_allocator);
// defer test_allocator.free(first_value);
// try testing.expectEqualStrings("Ok(10)", first_value);
// }
test "e_low_level_lambda - List.concat with strings (refcounted elements)" {
const src =
\\x = List.concat(["hello", "world"], ["foo", "bar"])