Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 296 additions & 0 deletions src/language/builtins/BuiltinsList.re
Original file line number Diff line number Diff line change
Expand Up @@ -2880,6 +2880,302 @@ let go: ([?], [?], [?]) -> [?] =
Fresh.(Exp.(var("zip")));
},
},
{
str: {|fix unique -> fun xs ->
fold_left(xs, fun (seen, x) -> if mem(seen, x) then seen else seen @ [x], [])|},
name: "unique",
arg: List(unknown(Internal)),
ret: List(unknown(Internal)),
imp: {
Fresh.(
Exp.(
fix_f(
Pat.var("unique"),
fn(
Pat.var("xs"),
ap(
Forward,
var("fold_left"),
tuple([
var("xs"),
fn(
Pat.tuple([Pat.var("seen"), Pat.var("x")]),
if_(
ap(
Forward,
var("mem"),
tuple([var("seen"), var("x")]),
),
var("seen"),
ap(
Forward,
var("append"),
tuple([var("seen"), list_lit([var("x")])]),
),
),
None,
None,
),
list_lit([]),
]),
),
None,
None,
),
None,
)
)
);
},
},
{
str: {hazel|fix pivot_table -> fun (table, new_col, index, value) ->
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter order in the str field (table, new_col, index, value) does not match the order in the imp field at lines 3045-3049, which uses (table, new_col, index, value). However, the implementation appears to expect the parameters in the order (table, index, new_col, value) based on how they're used in the test (line 466-468: first extracts row, then column). Please verify the intended parameter order is consistent between the string representation and the implementation.

Suggested change
str: {hazel|fix pivot_table -> fun (table, new_col, index, value) ->
str: {hazel|fix pivot_table -> fun (table, index, new_col, value) ->

Copilot uses AI. Check for mistakes.
let indices = map(table, index) |> unique in
let new_cols = map(table, new_col) |> unique in

map(indices, fun idx ->
(index=idx) ...
(map(new_cols, fun col ->
(label=col,
value=filter(table, fun r -> index(r) == idx && new_col(r) == col)
|>value)
) |> from_lvs))|hazel},
name: "pivot_table",
arg:
Prod([
list(unknown(Internal)),
arrow(unknown(Internal), unknown(Internal)),
arrow(unknown(Internal), unknown(Internal)),
arrow(unknown(Internal), unknown(Internal)),
]),
ret: List(unknown(Internal)),
imp: {
open Fresh;
open Exp;
let indices =
ap(
Reverse,
var("unique"),
ap(Forward, var("map"), tuple([var("table"), var("index")])),
);
let new_cols =
ap(
Reverse,
var("unique"),
ap(
Forward,
var("map"),
tuple([var("table"), var("new_col")]),
),
);

// filter(table, fun r -> index(r) == idx && new_col(r) == col)
let filtered_values =
ap(
Forward,
var("filter"),
tuple([
var("table"),
fn(
Pat.var("r"),
bin_op(
Bool(And),
bin_op(
Poly(Equals),
ap(Forward, var("index"), var("r")),
var("idx"),
),
bin_op(
Poly(Equals),
ap(Forward, var("new_col"), var("r")),
var("col"),
),
),
None,
None,
),
]),
);
/* (label=col,
value=filter(table, fun r -> index(r) == idx && new_col(r) == col)
|>value) */
let lvs =
tuple([
tup_label(label("label"), var("col")),
tup_label(
label("value"),
ap(Forward, var("value"), filtered_values),
),
]);

let from_lvs =
ap(
Reverse,
var("from_lvs"),
ap(
Forward,
var("map"),
tuple([
var("new_cols"),
fn(Pat.var("col"), lvs, None, None),
]),
),
);

let mapped =
ap(
Forward,
var("map"),
tuple([
var("indices"),
fn(
Pat.var("idx"),
tuple_extension(
tuple([tup_label(label("index"), var("idx"))]),
from_lvs,
),
None,
None,
),
]),
);

let fn =
fn(
Pat.tuple([
Pat.var("table"),
Pat.var("new_col"),
Pat.var("index"),
Pat.var("value"),
]),
let_(
Pat.var("indices"),
indices,
let_(Pat.var("new_cols"), new_cols, mapped),
),
None,
None,
);
fix_f(Pat.var("pivot_table"), fn, None);
},
},
{
str: {|fix group_on_key -> fun (xs, f) -> fold_left(xs, fun (acc, x) ->
let update_groups = fix update_groups -> fun (acc, key, x) ->
case acc
| [] => [(key, [x])]
| (k, g) :: acc => if k == key then (k, x :: g) :: acc else (k, g) :: update_groups(acc, key, x)
end in update_groups(acc, f(x), x), [])|},
name: "group_on_key",
arg:
Prod([
list(unknown(Internal)),
arrow(unknown(Internal), unknown(Internal)),
]),
ret: List(prod([unknown(Internal), list(unknown(Internal))])),
imp: {
Fresh.(
Exp.(
fix_f(
Pat.var("group_on_key"),
fn(
Pat.tuple([Pat.var("xs"), Pat.var("f")]),
ap(
Forward,
var("fold_left"),
tuple([
var("xs"),
let_(
Pat.var("update_groups"),
fix_f(
Pat.var("update_groups"),
fn(
Pat.tuple([
Pat.var("acc"),
Pat.var("key"),
Pat.var("x"),
]),
match(
var("acc"),
[
(
Pat.list_lit([]),
list_lit([
tuple([
var("key"),
list_lit([var("x")]),
]),
]),
),
(
Pat.cons(
Pat.tuple([Pat.var("k"), Pat.var("g")]),
Pat.var("acc_tail"),
),
if_(
bin_op(
Poly(Equals),
var("k"),
var("key"),
),
cons(
tuple([
var("k"),
list_concat(
var("g"),
list_lit([var("x")]),
),
]),
var("acc_tail"),
),
cons(
tuple([var("k"), var("g")]),
ap(
Forward,
var("update_groups"),
tuple([
var("acc_tail"),
var("key"),
var("x"),
]),
),
),
),
),
],
),
None,
Some("update_groups+"),
),
None,
),
fn(
Pat.tuple([Pat.var("acc"), Pat.var("x")]),
ap(
Forward,
var("update_groups"),
tuple([
var("acc"),
ap(Forward, var("f"), var("x")),
var("x"),
]),
),
None,
None,
),
),
list_lit([]),
]),
),
None,
Some("group_on_key+"),
),
None,
)
)
);
},
},
]
// De-alias all aliases
|> Util.ListUtil.map_with_history((prev, curr) =>
Expand Down
30 changes: 30 additions & 0 deletions test/evaluator/Test_Evaluator_Builtins_Lists.re
Original file line number Diff line number Diff line change
Expand Up @@ -446,5 +446,35 @@ let tests = (
);
},
),
test_case("unique", `Quick, () =>
parse_and_evaluate_test({|[1, 2, 3]|}, {|unique([1, 2, 2, 3, 1, 3])|})
),
test_case("group_on_key by parity", `Quick, () =>
parse_and_evaluate_test(
{|[(1, [1, 3]), (0, [2, 4])]|},
{|group_on_key([1, 2, 3, 4], fun x -> int_mod(x, 2))|},
)
),
test_case("pivot_table", `Quick, () =>
parse_and_evaluate_test(
{|
[(index="x", `A`=2, `B`=1, `C`=0), (index="y", `A`=1, `B`=1, `C`=1)]
|},
{|pivot_table(
[
("A", "x"),
("A", "y"),
("B", "x"),
("B", "y"),
("A", "x"),
("C", "y")
],
fun (r, _) -> r,
fun (_, c) -> c,
length
)
|},
)
),
],
);