Repository: tweag/nickel
Pull request #2082 (2024-10-28 14:56:28)

[RFC007] Fix the representation of Ast: remove inner indirection 
Depends on #2080.

`bytecode::ast::Ast` wasn't following the general approach that we set for designing other datastructures of the ast: use references within enums to avoid size bloat, but use owned data in structures to avoid indirection (since structures are packed).

Ast was doing something different, pushing the reference inside the individual fields to make a relatively thin pointer around `Node` and store that datastructure inline in the `Node` enum. While this essentially doesn't make a huge difference, we rather follow our simple general rule. Additionally, other data structures such as `bytecode::ast:typ::Type` follows the simple general rule, and we would rather do the same thing everywhere in a consistent manner.

Doing so, this commit also cleans a bit the interface of `AstAlloc` to reflect the ownership change and to get rid of some variations of methods in the sake of simplicity.

github-actions[bot]: <h2><a href="https://bencher.dev/perf/nickel/reports/9a76ba7d-08ff-40af-a99a-2b7a8934b33e?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel"><img src="https://bencher.dev/favicon.svg" width="24" height="24" alt="🐰" /> Bencher Report</a></h2><table><tr><td>Branch</td><td><a href="https://bencher.dev/perf/nickel/branches/2082-merge?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">2082/merge</a></td></tr><tr><td>Testbed</td><td><a href="https://bencher.dev/perf/nickel/testbeds/ubuntu-latest?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">ubuntu-latest</a></td></tr></table><blockquote><p><b>⚠️ WARNING:</b> The following Measure does not have a Threshold. Without a Threshold, no Alerts will ever be generated!</p><ul><li><a href="https://bencher.dev/perf/nickel/measures/latency?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">Latency</a></li></ul><p><a href="https://bencher.dev/console/projects/nickel/thresholds/add?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">Click here to create a new Threshold</a><br/>For more information, see <a href="https://bencher.dev/docs/explanation/thresholds/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">the Threshold documentation</a>.<br/>To only post results if a Threshold exists, set <a href="https://bencher.dev/docs/explanation/bencher-run/#--ci-only-thresholds?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">the <code lang="rust">--ci-only-thresholds</code> CLI flag</a>.</p></blockquote><details><summary>Click to view all benchmark results</summary><br/><table><thead><tr><th>Benchmark</th><th><a href="https://bencher.dev/perf/nickel/measures/latency?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">Latency</a></th><th>nanoseconds (ns)</th></tr></thead><tbody><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/fibonacci-10?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">fibonacci 10</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=3cfaf19e-c9c5-4341-95cd-703456d0fc9f&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>493,580.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/foldl-arrays-50?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">foldl arrays 50</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=95048806-9bf2-49e4-88f2-dbcd66cbf68a&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>1,724,100.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/foldl-arrays-500?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">foldl arrays 500</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=15373c31-496e-4f43-bbb4-466ef49fdd87&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>6,624,700.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/foldr-strings-50?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">foldr strings 50</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=87746452-c4df-48ea-b5e9-8d66f82ca556&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>7,019,600.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/foldr-strings-500?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">foldr strings 500</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=7f141ea0-9fbb-4305-bb63-78f163e546d6&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>61,966,000.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/generate-normal-250?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">generate normal 250</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=8c154d79-dc68-4d93-abb3-83183a62f6a2&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>46,339,000.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/generate-normal-50?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">generate normal 50</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=6d0222f6-0b85-4eaf-8c5c-e8957c522ef9&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>2,143,500.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/generate-normal-unchecked-1000?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">generate normal unchecked 1000</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=be0d3f87-d089-4b81-af44-3bbc6e831697&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>3,430,800.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/generate-normal-unchecked-200?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">generate normal unchecked 200</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=2946df33-45e4-4282-b749-040582021fe3&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>759,310.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/pidigits-100?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">pidigits 100</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=5be25c57-d46f-4b48-b6f0-dee01b84220a&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>3,193,400.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/pipe-normal-20?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">pipe normal 20</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=7178a975-b548-4e78-8c84-4f3802668825&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>1,496,900.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/pipe-normal-200?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">pipe normal 200</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=6884f292-8796-44a8-a30c-d3cb2ed987f6&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>10,111,000.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/product-30?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">product 30</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=2f5f7f6a-4e54-4542-aa09-da5671953fbb&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>827,060.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/scalar-10?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">scalar 10</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=211bb66a-58f8-488b-97e4-7ea032870038&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>1,503,600.00</td></tr><tr><td><a href="https://bencher.dev/perf/nickel/benchmarks/sum-30?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">sum 30</a></td><td>📈 <a href="https://bencher.dev/perf/nickel?branches=86c4b47e-b136-4195-a22e-ed04c03bc41a&heads=f35b29c5-ce43-48f5-8be3-57ce0b176c19&testbeds=66263ed0-9556-4385-9f30-26466ee26211&benchmarks=3f1710c5-ce75-4dfb-aadb-1435fed8a093&measures=2b5b9371-696f-422d-b1a5-262ce8157b6b&start_time=1727536107000&end_time=1730128107000&report=9a76ba7d-08ff-40af-a99a-2b7a8934b33e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">view plot</a><br/>⚠️ NO THRESHOLD</td><td>821,120.00</td></tr></tbody></table></details><a href="https://bencher.dev/perf/nickel/reports/9a76ba7d-08ff-40af-a99a-2b7a8934b33e?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nickel">🐰 View full continuous benchmarking report in Bencher</a><div id="bencher.dev/projects/nickel/id/2082-merge/ubuntu-latest/rust_criterion"></div>
dpulls[bot]: :tada: All dependencies have been resolved !

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 1efc85d3..aa0ad30f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -177,28 +177,6 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
 
-[[package]]
-name = "async-stream"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
-dependencies = [
- "async-stream-impl",
- "futures-core",
- "pin-project-lite",
-]
-
-[[package]]
-name = "async-stream-impl"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
-dependencies = [
- "proc-macro2 1.0.78",
- "quote 1.0.35",
- "syn 2.0.52",
-]
-
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -317,12 +295,6 @@ version = "1.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f"
 
-[[package]]
-name = "bytes"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
-
 [[package]]
 name = "cast"
 version = "0.3.0"
@@ -331,9 +303,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
 [[package]]
 name = "cc"
-version = "1.1.24"
+version = "1.1.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938"
+checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
 dependencies = [
  "shlex",
 ]
@@ -1454,7 +1426,7 @@ dependencies = [
  "petgraph",
  "pico-args",
  "regex",
- "regex-syntax 0.8.2",
+ "regex-syntax 0.8.5",
  "string_cache",
  "term",
  "tiny-keccak",
@@ -2478,25 +2450,25 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.6"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
  "regex-automata",
- "regex-syntax 0.8.2",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
 name = "regex-automata"
-version = "0.4.6"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax 0.8.2",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
@@ -2507,9 +2479,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.2"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "rgb"
@@ -2946,7 +2918,7 @@ dependencies = [
  "once_cell",
  "onig",
  "plist",
- "regex-syntax 0.8.2",
+ "regex-syntax 0.8.5",
  "serde",
  "serde_derive",
  "serde_json",
@@ -3148,30 +3120,6 @@ dependencies = [
  "syn 2.0.52",
 ]
 
-[[package]]
-name = "tokio-stream"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
-dependencies = [
- "futures-core",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tokio-test"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719"
-dependencies = [
- "async-stream",
- "bytes",
- "futures-core",
- "tokio",
- "tokio-stream",
-]
-
 [[package]]
 name = "toml"
 version = "0.8.14"
@@ -3208,9 +3156,9 @@ dependencies = [
 
 [[package]]
 name = "topiary-core"
-version = "0.4.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b26541394bdbcd5a039deb917b2096a69c6604f733f0d41f534fc6975987040d"
+checksum = "0236570d34c4e60d88e6bce8446f6d6fd51f5e1e4bdea68cdcf0d1ff5c89776d"
 dependencies = [
  "futures",
  "itertools 0.11.0",
@@ -3220,22 +3168,21 @@ dependencies = [
  "serde",
  "serde_json",
  "tokio",
- "tokio-test",
  "topiary-tree-sitter-facade",
  "topiary-web-tree-sitter-sys",
 ]
 
 [[package]]
 name = "topiary-queries"
-version = "0.4.2"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "103bcbfd5c605f2ef94074ae4950473f9dcf159f8bb01959a13856f96a2ffc8c"
+checksum = "dea6b8d21ab29eeb34fca55e384ddb6507261019e075d78812de1a1465ac210c"
 
 [[package]]
 name = "topiary-tree-sitter-facade"
-version = "0.4.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea7c870d9a844bbba0f55be40eaa89624a9f627c155cd22eea70dd808bd71b3d"
+checksum = "2eb978cfd5d4686e9193eed02c6c3abe8aac98e9d4942aaaf83ea472fc9b32d6"
 dependencies = [
  "js-sys",
  "topiary-web-tree-sitter-sys",
@@ -3246,9 +3193,9 @@ dependencies = [
 
 [[package]]
 name = "topiary-web-tree-sitter-sys"
-version = "0.4.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc9e35caeeb7f07e97b71ee7ea204c89516e7cc490d1c2ae318eca3ff3c08d46"
+checksum = "7e6ec4ec3a2426af1ff74688cd19c841a1e852403665d96b455946cb491cb0b6"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -3258,9 +3205,9 @@ dependencies = [
 
 [[package]]
 name = "tree-sitter"
-version = "0.20.10"
+version = "0.22.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d"
+checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca"
 dependencies = [
  "cc",
  "regex",
@@ -3268,9 +3215,9 @@ dependencies = [
 
 [[package]]
 name = "tree-sitter-nickel"
-version = "0.2.0"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "449daa9ac0e2a18d7243eaf21d7299d6683ce6750ca225ebd557412d22d106cf"
+checksum = "5e8b3a80d772b47cef3145983c0f431f7af2c93088695c516d7a89e68216f854"
 dependencies = [
  "cc",
  "tree-sitter",
diff --git a/Cargo.toml b/Cargo.toml
index 73cb993b..0074a844 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -103,14 +103,9 @@ bumpalo = "3.16.0"
 metrics = "0.21"
 metrics-util = "0.15"
 
-topiary-core = "0.4.0"
-topiary-queries = { version = "=0.4.2", default-features = false, features = ["nickel"] }
-# This version should agree with the topiary-query version: Nickel queries
-# target a specific version of the Nickel grammar (though that dependency
-# doesn't appear explicitly in `topiary-queries`'s Cargo.toml file). For now you
-# might have to look at Topiary's commit messages, but in a near future, we'll
-# try to make this data more accessible, e.g. in the query file
-tree-sitter-nickel = "=0.2.0"
+topiary-core = "0.5.1"
+topiary-queries = { version = "0.5.1", default-features = false, features = ["nickel"] }
+tree-sitter-nickel = "0.3.0"
 tempfile = "3.5.0"
 
 [profile.dev.package.lalrpop]
diff --git a/core/benches/arrays/fold.ncl b/core/benches/arrays/fold.ncl
index 58ba6d9b..c91fad4d 100644
--- a/core/benches/arrays/fold.ncl
+++ b/core/benches/arrays/fold.ncl
@@ -1,8 +1,7 @@
-let letter | Number -> std.string.Character
-  = fun n =>
-    let letters = std.string.characters "abcdefghijklmnopqrstuvwxyz" in
-    std.array.at (n % 26) letters
-  in
+let letter | Number -> std.string.Character = fun n =>
+  let letters = std.string.characters "abcdefghijklmnopqrstuvwxyz" in
+  std.array.at (n % 26) letters
+in
 
 {
   right = {
diff --git a/core/benches/arrays/generate.ncl b/core/benches/arrays/generate.ncl
index bc7323a9..92b06e86 100644
--- a/core/benches/arrays/generate.ncl
+++ b/core/benches/arrays/generate.ncl
@@ -10,7 +10,8 @@ let g = fun n => n * 2 + 5 in
     run = fun n => generate n g,
   },
   checked = {
-    generate_with_contract | forall a. Number -> (Number -> a) -> Array a
+    generate_with_contract
+      | forall a. Number -> (Number -> a) -> Array a
       = fun n g =>
         if n == 0 then
           []
diff --git a/core/benches/arrays/primes.ncl b/core/benches/arrays/primes.ncl
index 7d06c7ff..aa563608 100644
--- a/core/benches/arrays/primes.ncl
+++ b/core/benches/arrays/primes.ncl
@@ -6,11 +6,12 @@ let range
       []
     else
       std.array.generate (fun x => x + start) (end - start)
-  in
+in
 
-let is_prime | doc "Returns true if the argument is a prime number."
+let is_prime
+  | doc "Returns true if the argument is a prime number."
   = fun x => x > 1 && std.array.all (fun d => x % d != 0) (range 2 (x - 1))
-  in
+in
 
 let Prime = std.contract.from_predicate is_prime in
 
@@ -34,7 +35,7 @@ let primes
         loop (x + 1) (drop_multiples x xs)
     in
     loop 2 (range 2 max)
-  in
+in
 
 {
   run = primes
diff --git a/core/benches/arrays/random.ncl b/core/benches/arrays/random.ncl
index 1b876e9b..d1a5b561 100644
--- a/core/benches/arrays/random.ncl
+++ b/core/benches/arrays/random.ncl
@@ -1,6 +1,7 @@
-let mgc | doc "A multiplicative congruential pseudorandom number generator (MCG)."
+let mgc
+  | doc "A multiplicative congruential pseudorandom number generator (MCG)."
   = fun params x => (params.a * x + params.c) % params.m
-  in
+in
 
 # NOTE: These parameters are from "Table 7: Good multipliers for MCGs with m = 2^32"
 # of the 2021 paper "Computationally Easy, Spectrally Good Multipliers for
diff --git a/core/benches/arrays/sum.ncl b/core/benches/arrays/sum.ncl
index 180ead32..8b1f805e 100644
--- a/core/benches/arrays/sum.ncl
+++ b/core/benches/arrays/sum.ncl
@@ -1,9 +1,8 @@
-let rec sum | Array Number -> Number
-  = match {
-    [] => 0,
-    [x, ..xs] => x + sum xs,
-  }
-  in
+let rec sum | Array Number -> Number = match {
+  [] => 0,
+  [x, ..xs] => x + sum xs,
+}
+in
 {
   run = fun n => std.array.generate (fun x => x + 1) n |> sum
 }
diff --git a/core/benches/mantis/jobs/mantis.ncl b/core/benches/mantis/jobs/mantis.ncl
index 5851d7ec..03b8b590 100644
--- a/core/benches/mantis/jobs/mantis.ncl
+++ b/core/benches/mantis/jobs/mantis.ncl
@@ -121,13 +121,12 @@ fun params =>
       & {
         reschedule = params.reschedule,
 
-        task.telegraf | Telegraf
-          = {
-            #infinte rec?
-            # #namespace = namespace,
-            name = "%{std.string.from_enum params.role}-${NOMAD_ALLOC_INDEX}",
-            prometheusPort = "metrics",
-          },
+        task.telegraf | Telegraf = {
+          #infinte rec?
+          # #namespace = namespace,
+          name = "%{std.string.from_enum params.role}-${NOMAD_ALLOC_INDEX}",
+          prometheusPort = "metrics",
+        },
 
         # Do we really need this?
         #task.mantis | tasks.#Mantis = {
diff --git a/core/src/bytecode/ast/compat.rs b/core/src/bytecode/ast/compat.rs
index 3a26c9d8..e1de29bd 100644
--- a/core/src/bytecode/ast/compat.rs
+++ b/core/src/bytecode/ast/compat.rs
@@ -26,7 +26,7 @@ impl<'ast> FromMainline<'ast, term::pattern::Pattern> for &'ast Pattern<'ast> {
         alloc: &'ast AstAlloc,
         pattern: &term::pattern::Pattern,
     ) -> &'ast Pattern<'ast> {
-        alloc.move_pattern(pattern.to_ast(alloc))
+        alloc.pattern(pattern.to_ast(alloc))
     }
 }
 
@@ -108,7 +108,11 @@ impl<'ast> FromMainline<'ast, term::pattern::ArrayPattern> for PatternData<'ast>
 impl<'ast> FromMainline<'ast, term::pattern::EnumPattern> for PatternData<'ast> {
     fn from_mainline(alloc: &'ast AstAlloc, enum_pat: &term::pattern::EnumPattern) -> Self {
         let pattern = enum_pat.pattern.as_ref().map(|pat| (**pat).to_ast(alloc));
-        PatternData::Enum(alloc.enum_pattern(enum_pat.tag, pattern, enum_pat.pos))
+        PatternData::Enum(alloc.enum_pattern(EnumPattern {
+            tag: enum_pat.tag,
+            pattern,
+            pos: enum_pat.pos,
+        }))
     }
 }
 
@@ -125,7 +129,10 @@ impl<'ast> FromMainline<'ast, term::pattern::ConstantPattern> for PatternData<'a
             term::pattern::ConstantPatternData::Null => ConstantPatternData::Null,
         };
 
-        PatternData::Constant(alloc.constant_pattern(data, pattern.pos))
+        PatternData::Constant(alloc.constant_pattern(ConstantPattern {
+            data,
+            pos: pattern.pos,
+        }))
     }
 }
 
@@ -241,13 +248,13 @@ impl<'ast> FromMainline<'ast, MainlineRecordRowsUnr> for RecordRowsUnr<'ast> {
     }
 }
 
-impl<'ast> FromMainline<'ast, term::Term> for &'ast Node<'ast> {
+impl<'ast> FromMainline<'ast, term::Term> for Node<'ast> {
     fn from_mainline(alloc: &'ast AstAlloc, term: &term::Term) -> Self {
         use term::Term;
 
         match term {
-            Term::Null => alloc.null(),
-            Term::Bool(b) => alloc.bool(*b),
+            Term::Null => Node::Null,
+            Term::Bool(b) => Node::Bool(*b),
             Term::Num(n) => alloc.number(n.clone()),
             Term::Str(s) => alloc.string(s),
             Term::StrChunks(chunks) => alloc.str_chunks(
@@ -261,10 +268,7 @@ impl<'ast> FromMainline<'ast, term::Term> for &'ast Node<'ast> {
                     })
                     .rev(),
             ),
-            Term::Fun(id, body) => alloc.fun(
-                alloc.generic_arena.alloc(Pattern::any(*id)),
-                body.to_ast(alloc),
-            ),
+            Term::Fun(id, body) => alloc.fun(Pattern::any(*id), body.to_ast(alloc)),
             Term::FunPattern(pat, body) => alloc.fun(pat.to_ast(alloc), body.to_ast(alloc)),
             Term::Let(bindings, body, attrs) => alloc.let_binding(
                 bindings
@@ -312,7 +316,7 @@ impl<'ast> FromMainline<'ast, term::Term> for &'ast Node<'ast> {
 
                 alloc.app(fun.to_ast(alloc), args.into_iter().rev())
             }
-            Term::Var(id) => alloc.var(*id),
+            Term::Var(id) => Node::Var(*id),
             Term::Enum(id) => alloc.enum_variant(*id, None),
             Term::EnumVariant { tag, arg, attrs: _ } => {
                 alloc.enum_variant(*tag, Some(arg.to_ast(alloc)))
@@ -412,7 +416,7 @@ impl<'ast> FromMainline<'ast, term::Term> for &'ast Node<'ast> {
             }
             Term::Import { path, format } => alloc.import(path.clone(), *format),
             Term::ResolvedImport(_) => panic!("didn't expect a resolved import at parsing stage"),
-            Term::Type { typ, .. } => alloc.typ_move(typ.to_ast(alloc)),
+            Term::Type { typ, .. } => alloc.typ(typ.to_ast(alloc)),
             Term::CustomContract(_) => panic!("didn't expect a custom contract at parsing stage"),
             Term::ParseError(error) => alloc.parse_error(error.clone()),
             Term::RuntimeError(_) => panic!("didn't expect a runtime error at parsing stage"),
@@ -425,7 +429,16 @@ impl<'ast> FromMainline<'ast, term::Term> for &'ast Node<'ast> {
 
 impl<'ast> FromMainline<'ast, term::RichTerm> for Ast<'ast> {
     fn from_mainline(alloc: &'ast AstAlloc, rterm: &term::RichTerm) -> Self {
-        alloc.ast(rterm.as_ref().to_ast(alloc), rterm.pos)
+        Ast {
+            node: rterm.as_ref().to_ast(alloc),
+            pos: rterm.pos,
+        }
+    }
+}
+
+impl<'ast> FromMainline<'ast, term::RichTerm> for &'ast Ast<'ast> {
+    fn from_mainline(alloc: &'ast AstAlloc, rterm: &term::RichTerm) -> Self {
+        alloc.ast(rterm.to_ast(alloc))
     }
 }
 
@@ -869,7 +882,7 @@ impl<'ast> FromAst<MatchBranch<'ast>> for term::MatchBranch {
     fn from_ast(branch: &MatchBranch<'ast>) -> Self {
         term::MatchBranch {
             pattern: branch.pattern.to_mainline(),
-            guard: branch.guard.map(|ast| ast.to_mainline()),
+            guard: branch.guard.as_ref().map(|ast| ast.to_mainline()),
             body: branch.body.to_mainline(),
         }
     }
@@ -1050,9 +1063,9 @@ impl<'ast> FromAst<Node<'ast>> for term::Term {
 
                 Term::StrChunks(chunks)
             }
-            Node::Fun(pat, body) => match pat.data {
+            Node::Fun { arg, body } => match arg.data {
                 PatternData::Any(id) => Term::Fun(id, body.to_mainline()),
-                _ => Term::FunPattern((*pat).to_mainline(), body.to_mainline()),
+                _ => Term::FunPattern((*arg).to_mainline(), body.to_mainline()),
             },
             Node::Let {
                 bindings,
@@ -1184,7 +1197,7 @@ impl<'ast> FromAst<Node<'ast>> for term::Term {
 
 impl<'ast> FromAst<Ast<'ast>> for term::RichTerm {
     fn from_ast(ast: &Ast<'ast>) -> Self {
-        let mut result = term::RichTerm::new(ast.node.to_mainline(), *ast.pos);
+        let mut result = term::RichTerm::new(ast.node.to_mainline(), ast.pos);
         // See [^merge-label-span]
         if let term::Term::Op2(term::BinaryOp::Merge(ref mut label), _, _) =
             term::SharedTerm::make_mut(&mut result.term)
@@ -1197,3 +1210,9 @@ impl<'ast> FromAst<Ast<'ast>> for term::RichTerm {
         result
     }
 }
+
+impl<'ast> FromAst<&'ast Ast<'ast>> for term::RichTerm {
+    fn from_ast(ast: &&'ast Ast<'ast>) -> Self {
+        FromAst::from_ast(*ast)
+    }
+}
diff --git a/core/src/bytecode/ast/mod.rs b/core/src/bytecode/ast/mod.rs
index 9005db43..7d7ede49 100644
--- a/core/src/bytecode/ast/mod.rs
+++ b/core/src/bytecode/ast/mod.rs
@@ -33,13 +33,12 @@ use typ::*;
 
 /// A Nickel AST. Contains a root node and a span.
 ///
-/// Both members are references so that `Ast` is cheap to clone - it's in fact `Copy` - and to
-/// store. Additionally, we don't expect to access the span much on the happy path, so the
-/// indirection should be ok.
-#[derive(Clone, Copy, Debug, PartialEq)]
+//TODO: we don't expect to access the span much on the happy path. Should we add an indirection
+//through a reference?
+#[derive(Clone, Debug, PartialEq)]
 pub struct Ast<'ast> {
-    node: &'ast Node<'ast>,
-    pos: &'ast TermPos,
+    node: Node<'ast>,
+    pos: TermPos,
 }
 
 /// A node of the Nickel AST.
@@ -78,18 +77,21 @@ pub enum Node<'ast> {
     StrChunks(&'ast [StrChunk<Ast<'ast>>]),
 
     /// A function.
-    Fun(&'ast Pattern<'ast>, Ast<'ast>),
+    Fun {
+        arg: &'ast Pattern<'ast>,
+        body: &'ast Ast<'ast>,
+    },
 
     /// A let-binding.
     Let {
         bindings: &'ast [(Pattern<'ast>, Ast<'ast>)],
-        body: Ast<'ast>,
+        body: &'ast Ast<'ast>,
         rec: bool,
     },
 
     /// An application to one or more arguments.
     App {
-        fun: Ast<'ast>,
+        fun: &'ast Ast<'ast>,
         args: &'ast [Ast<'ast>],
     },
 
@@ -103,7 +105,7 @@ pub enum Node<'ast> {
     /// multiple arguments.
     EnumVariant {
         tag: LocIdent,
-        arg: Option<Ast<'ast>>,
+        arg: Option<&'ast Ast<'ast>>,
     },
 
     /// A record.
@@ -111,9 +113,9 @@ pub enum Node<'ast> {
 
     /// An if-then-else expression.
     IfThenElse {
-        cond: Ast<'ast>,
-        then_branch: Ast<'ast>,
-        else_branch: Ast<'ast>,
+        cond: &'ast Ast<'ast>,
+        then_branch: &'ast Ast<'ast>,
+        else_branch: &'ast Ast<'ast>,
     },
 
     /// A match expression. This expression is still to be applied to an argument to match on.
@@ -134,7 +136,7 @@ pub enum Node<'ast> {
     /// A term with a type and/or contract annotation.
     Annotated {
         annot: &'ast Annotation<'ast>,
-        inner: Ast<'ast>,
+        inner: &'ast Ast<'ast>,
     },
 
     /// An import.
@@ -244,79 +246,68 @@ impl AstAlloc {
         }
     }
 
-    pub fn null(&self) -> &Node<'_> {
-        self.generic_arena.alloc(Node::Null)
+    pub fn node<'ast>(&'ast self, node: Node<'ast>) -> &'ast Node<'ast> {
+        self.generic_arena.alloc(node)
     }
 
-    pub fn bool(&self, value: bool) -> &Node<'_> {
-        self.generic_arena.alloc(Node::Bool(value))
+    pub fn number(&self, number: Number) -> Node<'_> {
+        Node::Number(self.number_arena.alloc(number))
     }
 
-    pub fn number(&self, number: Number) -> &Node<'_> {
-        let number = self.number_arena.alloc(number);
-        self.generic_arena.alloc(Node::Number(number))
+    pub fn string<'ast>(&'ast self, s: &str) -> Node<'ast> {
+        Node::String(self.generic_arena.alloc_str(s))
     }
 
-    pub fn string<'ast>(&'ast self, s: &str) -> &'ast Node<'ast> {
-        let s = self.generic_arena.alloc_str(s);
-        self.generic_arena.alloc(Node::String(s))
-    }
-
-    pub fn str_chunks<'ast, I>(&'ast self, chunks: I) -> &'ast Node<'ast>
+    pub fn str_chunks<'ast, I>(&'ast self, chunks: I) -> Node<'ast>
     where
         I: IntoIterator<Item = StrChunk<Ast<'ast>>>,
         I::IntoIter: ExactSizeIterator,
     {
-        let chunks = self.generic_arena.alloc_slice_fill_iter(chunks);
-        self.generic_arena.alloc(Node::StrChunks(chunks))
+        Node::StrChunks(self.generic_arena.alloc_slice_fill_iter(chunks))
     }
 
-    pub fn fun<'ast>(&'ast self, pat: &'ast Pattern<'ast>, body: Ast<'ast>) -> &'ast Node<'ast> {
-        self.generic_arena.alloc(Node::Fun(pat, body))
+    pub fn fun<'ast>(&'ast self, pat: Pattern<'ast>, body: Ast<'ast>) -> Node<'ast> {
+        let arg = self.generic_arena.alloc(pat);
+        let body = self.generic_arena.alloc(body);
+        Node::Fun { arg, body }
     }
 
-    pub fn let_binding<'ast, I>(
-        &'ast self,
-        bindings: I,
-        body: Ast<'ast>,
-        rec: bool,
-    ) -> &'ast Node<'ast>
+    pub fn let_binding<'ast, I>(&'ast self, bindings: I, body: Ast<'ast>, rec: bool) -> Node<'ast>
     where
         I: IntoIterator<Item = (Pattern<'ast>, Ast<'ast>)>,
         I::IntoIter: ExactSizeIterator,
     {
         let bindings = self.generic_arena.alloc_slice_fill_iter(bindings);
-        self.generic_arena.alloc(Node::Let {
+        let body = self.generic_arena.alloc(body);
+
+        Node::Let {
             bindings,
             body,
             rec,
-        })
+        }
     }
 
-    pub fn app<'ast, I>(&'ast self, fun: Ast<'ast>, args: I) -> &'ast Node<'ast>
+    pub fn app<'ast, I>(&'ast self, fun: Ast<'ast>, args: I) -> Node<'ast>
     where
         I: IntoIterator<Item = Ast<'ast>>,
         I::IntoIter: ExactSizeIterator,
     {
-        let args = self.generic_arena.alloc_slice_fill_iter(args);
-        self.generic_arena.alloc(Node::App { fun, args })
-    }
-
-    pub fn var(&self, ident: LocIdent) -> &Node<'_> {
-        self.generic_arena.alloc(Node::Var(ident))
+        Node::App {
+            fun: self.generic_arena.alloc(fun),
+            args: self.generic_arena.alloc_slice_fill_iter(args),
+        }
     }
 
-    pub fn enum_variant<'ast>(
-        &'ast self,
-        tag: LocIdent,
-        arg: Option<Ast<'ast>>,
-    ) -> &'ast Node<'ast> {
-        self.generic_arena.alloc(Node::EnumVariant { tag, arg })
+    pub fn enum_variant<'ast>(&'ast self, tag: LocIdent, arg: Option<Ast<'ast>>) -> Node<'ast> {
+        Node::EnumVariant {
+            tag,
+            arg: arg.map(|arg| &*self.generic_arena.alloc(arg)),
+        }
     }
 
-    pub fn record<'ast>(&'ast self, record: Record<'ast>) -> &'ast Node<'ast> {
+    pub fn record<'ast>(&'ast self, record: Record<'ast>) -> Node<'ast> {
         let record = self.generic_arena.alloc(record);
-        self.generic_arena.alloc(Node::Record(record))
+        Node::Record(record)
     }
 
     pub fn record_data<'ast, Ss, Ds>(
@@ -345,71 +336,64 @@ impl AstAlloc {
         cond: Ast<'ast>,
         then_branch: Ast<'ast>,
         else_branch: Ast<'ast>,
-    ) -> &'ast Node<'ast> {
-        self.generic_arena.alloc(Node::IfThenElse {
-            cond,
-            then_branch,
-            else_branch,
-        })
+    ) -> Node<'ast> {
+        Node::IfThenElse {
+            cond: self.generic_arena.alloc(cond),
+            then_branch: self.generic_arena.alloc(then_branch),
+            else_branch: self.generic_arena.alloc(else_branch),
+        }
     }
 
-    pub fn match_expr<'ast, I>(&'ast self, branches: I) -> &'ast Node<'ast>
+    pub fn match_expr<'ast, I>(&'ast self, branches: I) -> Node<'ast>
     where
         I: IntoIterator<Item = MatchBranch<'ast>>,
         I::IntoIter: ExactSizeIterator,
     {
-        let branches = self.generic_arena.alloc_slice_fill_iter(branches);
-        self.generic_arena.alloc(Node::Match(Match { branches }))
+        Node::Match(Match {
+            branches: self.generic_arena.alloc_slice_fill_iter(branches),
+        })
     }
 
-    pub fn array<'ast, I>(&'ast self, elts: I) -> &'ast Node<'ast>
+    pub fn array<'ast, I>(&'ast self, elts: I) -> Node<'ast>
     where
         I: IntoIterator<Item = Ast<'ast>>,
         I::IntoIter: ExactSizeIterator,
     {
-        let elts = self.generic_arena.alloc_slice_fill_iter(elts);
-        self.generic_arena.alloc(Node::Array(elts))
+        Node::Array(self.generic_arena.alloc_slice_fill_iter(elts))
     }
 
-    pub fn prim_op<'ast, I>(&'ast self, op: PrimOp, args: I) -> &'ast Node<'ast>
+    pub fn prim_op<'ast, I>(&'ast self, op: PrimOp, args: I) -> Node<'ast>
     where
         I: IntoIterator<Item = Ast<'ast>>,
         I::IntoIter: ExactSizeIterator,
     {
         let op = self.generic_arena.alloc(op);
         let args = self.generic_arena.alloc_slice_fill_iter(args);
-        self.generic_arena.alloc(Node::PrimOpApp { op, args })
+        Node::PrimOpApp { op, args }
     }
 
-    pub fn annotated<'ast>(
-        &'ast self,
-        annot: Annotation<'ast>,
-        inner: Ast<'ast>,
-    ) -> &'ast Node<'ast> {
-        let annot = self.generic_arena.alloc(annot);
-        self.generic_arena.alloc(Node::Annotated { annot, inner })
-    }
-
-    pub fn import(&self, path: OsString, format: InputFormat) -> &Node<'_> {
-        let path = self.generic_arena.alloc(path);
-        self.generic_arena.alloc(Node::Import { path, format })
+    pub fn annotated<'ast>(&'ast self, annot: Annotation<'ast>, inner: Ast<'ast>) -> Node<'ast> {
+        Node::Annotated {
+            annot: self.generic_arena.alloc(annot),
+            inner: self.generic_arena.alloc(inner),
+        }
     }
 
-    pub fn typ<'ast>(&'ast self, typ: TypeUnr<'ast>, pos: TermPos) -> &'ast Node<'ast> {
-        let type_full = self.generic_arena.alloc(Type { typ, pos });
-        self.generic_arena.alloc(Node::Type(type_full))
+    pub fn import(&self, path: OsString, format: InputFormat) -> Node<'_> {
+        Node::Import {
+            path: self.generic_arena.alloc(path),
+            format,
+        }
     }
 
     /// As opposed to [Self::typ], this method takes an already constructed type and move it into
     /// the arena, instead of taking each constituent separately.
-    pub fn typ_move<'ast>(&'ast self, typ: Type<'ast>) -> &'ast Node<'ast> {
-        let typ = self.generic_arena.alloc(typ);
-        self.generic_arena.alloc(Node::Type(typ))
+    pub fn typ<'ast>(&'ast self, typ: Type<'ast>) -> Node<'ast> {
+        Node::Type(self.generic_arena.alloc(typ))
     }
 
-    pub fn typ_unr_move<'ast>(&'ast self, typ: TypeUnr<'ast>, pos: TermPos) -> &'ast Node<'ast> {
-        let type_full = self.generic_arena.alloc(Type { typ, pos });
-        self.generic_arena.alloc(Node::Type(type_full))
+    pub fn typ_from_unr<'ast>(&'ast self, typ: TypeUnr<'ast>, pos: TermPos) -> Node<'ast> {
+        Node::Type(self.generic_arena.alloc(Type { typ, pos }))
     }
 
     pub fn types<'ast, I>(&'ast self, types: I) -> &'ast [Type<'ast>]
@@ -428,53 +412,30 @@ impl AstAlloc {
         self.generic_arena.alloc(RecordRows(rrows))
     }
 
-    pub fn parse_error(&self, error: ParseError) -> &Node<'_> {
-        let error = self.error_arena.alloc(error);
-        self.generic_arena.alloc(Node::ParseError(error))
-    }
-
-    pub fn ast<'ast>(&'ast self, node: &'ast Node<'ast>, pos: TermPos) -> Ast<'ast> {
-        let pos = self.generic_arena.alloc(pos);
-        Ast { node, pos }
+    pub fn parse_error(&self, error: ParseError) -> Node<'_> {
+        Node::ParseError(self.error_arena.alloc(error))
     }
 
-    pub fn pattern<'ast>(
-        &'ast self,
-        data: PatternData<'ast>,
-        alias: Option<LocIdent>,
-        pos: TermPos,
-    ) -> &'ast Pattern<'ast> {
-        self.generic_arena.alloc(Pattern { data, alias, pos })
+    pub fn ast<'ast>(&'ast self, ast: Ast<'ast>) -> &'ast Ast<'ast> {
+        self.generic_arena.alloc(ast)
     }
 
-    pub fn move_pattern<'ast>(&'ast self, pattern: Pattern<'ast>) -> &'ast Pattern<'ast> {
+    pub fn pattern<'ast>(&'ast self, pattern: Pattern<'ast>) -> &'ast Pattern<'ast> {
         self.generic_arena.alloc(pattern)
     }
 
     pub fn enum_pattern<'ast>(
         &'ast self,
-        tag: LocIdent,
-        pattern: Option<Pattern<'ast>>,
-        pos: TermPos,
+        enum_pattern: EnumPattern<'ast>,
     ) -> &'ast EnumPattern<'ast> {
-        self.generic_arena.alloc(EnumPattern { tag, pattern, pos })
+        self.generic_arena.alloc(enum_pattern)
     }
 
     pub fn field_pattern<'ast>(
         &'ast self,
-        matched_id: LocIdent,
-        annotation: Annotation<'ast>,
-        default: Option<Ast<'ast>>,
-        pattern: Pattern<'ast>,
-        pos: TermPos,
+        field_pat: FieldPattern<'ast>,
     ) -> &'ast FieldPattern<'ast> {
-        self.generic_arena.alloc(FieldPattern {
-            matched_id,
-            annotation,
-            default,
-            pattern,
-            pos,
-        })
+        self.generic_arena.alloc(field_pat)
     }
 
     pub fn record_pattern<'ast, I>(
@@ -517,10 +478,9 @@ impl AstAlloc {
 
     pub fn constant_pattern<'ast>(
         &'ast self,
-        data: ConstantPatternData<'ast>,
-        pos: TermPos,
+        cst_pat: ConstantPattern<'ast>,
     ) -> &'ast ConstantPattern<'ast> {
-        self.generic_arena.alloc(ConstantPattern { data, pos })
+        self.generic_arena.alloc(cst_pat)
     }
 
     pub fn or_pattern<'ast, I>(&'ast self, patterns: I, pos: TermPos) -> &'ast OrPattern<'ast>
diff --git a/core/src/bytecode/ast/typ.rs b/core/src/bytecode/ast/typ.rs
index bf6ee4d0..252999dc 100644
--- a/core/src/bytecode/ast/typ.rs
+++ b/core/src/bytecode/ast/typ.rs
@@ -6,7 +6,7 @@ pub use mainline_typ::{EnumRowF, EnumRowsF, RecordRowF, RecordRowsF, TypeF};
 
 /// The recursive unrolling of a type, that is when we "peel off" the top-level layer to find the actual
 /// structure represented by an instantiation of `TypeF`.
-pub type TypeUnr<'ast> = TypeF<&'ast Type<'ast>, RecordRows<'ast>, EnumRows<'ast>, Ast<'ast>>;
+pub type TypeUnr<'ast> = TypeF<&'ast Type<'ast>, RecordRows<'ast>, EnumRows<'ast>, &'ast Ast<'ast>>;
 
 /// The recursive unrolling of a enum rows.
 pub type EnumRowsUnr<'ast> = EnumRowsF<&'ast Type<'ast>, &'ast EnumRows<'ast>>;
diff --git a/core/stdlib/std.ncl b/core/stdlib/std.ncl
index dc963457..8b9eb3d0 100644
--- a/core/stdlib/std.ncl
+++ b/core/stdlib/std.ncl
@@ -533,7 +533,7 @@
         "%
       = fun pred l =>
         let aux = fun acc x =>
-          if (pred x) then
+          if pred x then
             { right = acc.right @ [x], wrong = acc.wrong }
           else
             { right = acc.right, wrong = acc.wrong @ [x] }
@@ -654,8 +654,10 @@
         if length <= 1 then
           array
         else
-          let first = %array/at% array 0 in
-          let rest = %array/slice% 1 length array in
+          let
+            first = %array/at% array 0,
+            rest = %array/slice% 1 length array,
+          in
           [first] @ (flat_map (fun a => [v, a]) rest),
 
     slice
@@ -866,8 +868,10 @@
         "%
       = fun f array =>
         let last_index = %array/length% array - 1 in
-        let last = %array/at% array last_index in
-        let rest = %array/slice% 0 last_index array in
+        let
+          last = %array/at% array last_index,
+          rest = %array/slice% 0 last_index array,
+        in
         fold_right f last rest,
 
     zip_with
@@ -1850,50 +1854,49 @@
             let label_module = label in
             let attach_message = label_module.with_message "invalid array indexing" in
 
-            let ArrayIndexFirst =
-              %contract/custom% (fun label value =>
-                if %typeof% value == 'Number then
-                  let label =
-                    label
-                    |> attach_message
-                    |> label_module.append_note "Expected array index to be a positive integer, got %{%to_string% value} "
-                  in
-                  std.contract.check std.number.Nat label value
-                else
-                  'Ok value
-              )
-            in
-
-            let ArrayIndexSecond = fun type min_size =>
-              %contract/custom% (fun _label value =>
-                if %typeof% min_size == 'Number && %typeof% value == 'Array then
-                  let max_idx =
-                    type
-                    |> match {
-                      'Index => %array/length% value - 1,
-                      'Split => %array/length% value
-                    }
-                  in
-
-                  if min_size > max_idx then
-                    let index_as_str = %to_string% min_size in
-                    let max_as_str = %to_string% max_idx in
-                    let note =
-                      if %array/length% value == 0 then
-                        "Can't index into an empty array"
-                      else
-                        "Expected an array index between 0 and %{max_as_str} (included), got %{index_as_str}"
+            let
+              ArrayIndexFirst =
+                %contract/custom% (fun label value =>
+                  if %typeof% value == 'Number then
+                    let label =
+                      label
+                      |> attach_message
+                      |> label_module.append_note "Expected array index to be a positive integer, got %{%to_string% value} "
+                    in
+                    std.contract.check std.number.Nat label value
+                  else
+                    'Ok value
+                ),
+              ArrayIndexSecond = fun type min_size =>
+                %contract/custom% (fun _label value =>
+                  if %typeof% min_size == 'Number && %typeof% value == 'Array then
+                    let max_idx =
+                      type
+                      |> match {
+                        'Index => %array/length% value - 1,
+                        'Split => %array/length% value
+                      }
                     in
 
-                    'Error {
-                      message = "invalid array indexing",
-                      notes = [note],
-                    }
+                    if min_size > max_idx then
+                      let index_as_str = %to_string% min_size in
+                      let max_as_str = %to_string% max_idx in
+                      let note =
+                        if %array/length% value == 0 then
+                          "Can't index into an empty array"
+                        else
+                          "Expected an array index between 0 and %{max_as_str} (included), got %{index_as_str}"
+                      in
+
+                      'Error {
+                        message = "invalid array indexing",
+                        notes = [note],
+                      }
+                    else
+                      'Ok value
                   else
                     'Ok value
-                else
-                  'Ok value
-              )
+                ),
             in
 
             fun type =>
@@ -1923,59 +1926,56 @@
             let label_module = label in
             let attach_message = label_module.with_message "invalid array slice indexing" in
 
-            let SliceIndexFirst =
-              %contract/custom% (fun label value =>
-                if %typeof% value == 'Number then
-                  let label =
-                    label
-                    |> attach_message
-                    |> label_module.append_note "Expected the array slice start index to be a positive integer, got %{%to_string% value}"
-                  in
-                  std.contract.check std.number.Nat label value
-                else
-                  'Ok value
-              )
-            in
+            let
+              SliceIndexFirst =
+                %contract/custom% (fun label value =>
+                  if %typeof% value == 'Number then
+                    let label =
+                      label
+                      |> attach_message
+                      |> label_module.append_note "Expected the array slice start index to be a positive integer, got %{%to_string% value}"
+                    in
+                    std.contract.check std.number.Nat label value
+                  else
+                    'Ok value
+                ),
+              SliceIndexSecond = fun start =>
+                %contract/custom% (fun label value =>
+                  if %typeof% start == 'Number && %typeof% value == 'Number then
+                    if value < start then
+                      'Error {
+                        message = "invalid array slice indexing",
+                        notes = [
+                          "Expected the array slice indices to satisfy `start <= end`, but got %{%to_string% start} (start) and %{%to_string% value} (end)"
+                        ],
+                      }
+                    else
+                      let label =
+                        label
+                        |> attach_message
+                        |> label_module.append_note "Expected the array slice end index to be a positive integer, got %{%to_string% value}"
+                      in
+                      std.contract.check std.number.Nat label value
+                  else
+                    'Ok value
+                ),
+              ArraySliceArray = fun end_index =>
+                %contract/custom% (fun _label value =>
+                  if %typeof% end_index == 'Number
+                  && %typeof% value == 'Array
+                  && end_index > %array/length% value then
+                    let index_as_str = %to_string% end_index in
+                    let size_as_str = %to_string% (%array/length% value) in
 
-            let SliceIndexSecond = fun start =>
-              %contract/custom% (fun label value =>
-                if %typeof% start == 'Number && %typeof% value == 'Number then
-                  if value < start then
                     'Error {
                       message = "invalid array slice indexing",
                       notes = [
-                        "Expected the array slice indices to satisfy `start <= end`, but got %{%to_string% start} (start) and %{%to_string% value} (end)"
+                        "Expected the slice end index to be between 0 and %{size_as_str} (array's length), got %{index_as_str}"
                       ],
                     }
                   else
-                    let label =
-                      label
-                      |> attach_message
-                      |> label_module.append_note "Expected the array slice end index to be a positive integer, got %{%to_string% value}"
-                    in
-                    std.contract.check std.number.Nat label value
-                else
-                  'Ok value
-              )
-            in
-
-            let ArraySliceArray = fun end_index =>
-              %contract/custom% (fun _label value =>
-                if %typeof% end_index == 'Number
-                && %typeof% value == 'Array
-                && end_index > %array/length% value then
-                  let index_as_str = %to_string% end_index in
-                  let size_as_str = %to_string% (%array/length% value) in
-
-                  'Error {
-                    message = "invalid array slice indexing",
-                    notes = [
-                      "Expected the slice end index to be between 0 and %{size_as_str} (array's length), got %{index_as_str}"
-                    ],
-                  }
-                else
-                  'Ok value
-              )
+                    'Ok value
+                ),
             in
 
             DependentFun
@@ -2252,8 +2252,10 @@
         "%
       = fun f enum_value =>
         if %enum/is_variant% enum_value then
-          let tag = (%to_string% (%enum/get_tag% enum_value)) in
-          let mapped = f (%enum/get_arg% enum_value) in
+          let
+            tag = (%to_string% (%enum/get_tag% enum_value)),
+            mapped = f (%enum/get_arg% enum_value),
+          in
 
           %enum/make_variant% tag mapped
         else
@@ -3505,10 +3507,11 @@
         if length == 0 then
           ""
         else
-          let first = %array/at% fragments 0 in
-          let rest =
-            %array/slice% 1 length fragments
-            |> std.array.fold_left (fun acc s => acc ++ sep ++ s) ""
+          let
+            first = %array/at% fragments 0,
+            rest =
+              %array/slice% 1 length fragments
+              |> std.array.fold_left (fun acc s => acc ++ sep ++ s) "",
           in
           first ++ rest,
 
diff --git a/examples/arrays/arrays.ncl b/examples/arrays/arrays.ncl
index 4caba0e4..406d240a 100644
--- a/examples/arrays/arrays.ncl
+++ b/examples/arrays/arrays.ncl
@@ -2,25 +2,25 @@
 
 # Example array functions. This code is illustrative: prefer using the array
 # stdlib functions `std.array.map` and `std.array.fold_right` instead.
-let my_array_lib = {
-  map : forall a b. (a -> b) -> Array a -> Array b
+let rec
+  map
+    : forall a b. (a -> b) -> Array a -> Array b
     = fun f arr =>
       if arr == [] then
         []
       else
         let [head, ..tail] = arr in
         [f head] @ map f tail,
-
-  fold : forall a b. (a -> b -> b) -> b -> Array a -> b
+  fold
+    : forall a b. (a -> b -> b) -> b -> Array a -> b
     = fun f first arr =>
       if arr == [] then
         first
       else
         let [head, ..tail] = arr in
         f head (fold f first tail),
-}
 in
 # Compute `7!`
 [1, 2, 3, 4, 5, 6]
-|> my_array_lib.map ((+) 1)
-|> my_array_lib.fold (*) 1
+|> map ((+) 1)
+|> fold (*) 1
diff --git a/examples/config-gcc/config-gcc.ncl b/examples/config-gcc/config-gcc.ncl
index 874ec268..2ad76ec7 100644
--- a/examples/config-gcc/config-gcc.ncl
+++ b/examples/config-gcc/config-gcc.ncl
@@ -2,67 +2,62 @@
 
 # Validate and normalize gcc flags. They can be either a string `-Wextra` or
 # a structured value `{flag = "W", arg = "extra"}`. Arguments are not checked.
-let GccFlag =
-  let supported_flags = ["W", "c", "S", "e", "o"] in
-  let is_valid_flag
-    | doc "check if a string of length > 0 is a valid flag"
-    = fun string =>
-      std.array.elem (std.string.substring 0 1 string) supported_flags
+let
+  GccFlag =
+    let supported_flags = ["W", "c", "S", "e", "o"] in
+    let is_valid_flag
+      | doc "check if a string of length > 0 is a valid flag"
+      = fun string =>
+        std.array.elem (std.string.substring 0 1 string) supported_flags
     in
 
-  std.contract.custom (fun label =>
-    match {
-      value if std.is_string value && is_valid_flag value => 'Ok value,
-      { flag, arg } if std.array.elem flag supported_flags =>
-        # We normalize a record representation to a string
-        'Ok "%{flag}%{arg}",
-      value if std.is_string value =>
-        'Error { message = "unknown flag %{value}" },
-      { flag, arg = _ } =>
-        'Error { message = "unknown flag %{flag}" },
-      { .. } =>
-        'Error {
-          message = "bad record structure: missing field `flag` or `arg`",
-        },
-      _ => 'Error { message = "expected record or string" },
-    }
-  )
-in
-
-let Path =
-  let pattern = m%"^(.+)/([^/]+)$"% in
-  std.contract.from_validator (fun value =>
-    if std.is_string value then
-      if std.string.is_match pattern value then
-        'Ok
+    std.contract.custom (fun label =>
+      match {
+        value if std.is_string value && is_valid_flag value => 'Ok value,
+        { flag, arg } if std.array.elem flag supported_flags =>
+          # We normalize a record representation to a string
+          'Ok "%{flag}%{arg}",
+        value if std.is_string value =>
+          'Error { message = "unknown flag %{value}" },
+        { flag, arg = _ } =>
+          'Error { message = "unknown flag %{flag}" },
+        { .. } =>
+          'Error {
+            message = "bad record structure: missing field `flag` or `arg`",
+          },
+        _ => 'Error { message = "expected record or string" },
+      }
+    ),
+  Path =
+    let pattern = m%"^(.+)/([^/]+)$"% in
+    std.contract.from_validator (fun value =>
+      if std.is_string value then
+        if std.string.is_match pattern value then
+          'Ok
+        else
+          'Error { message = "invalid path" }
       else
-        'Error { message = "invalid path" }
-    else
-      'Error { message = "not a string" }
-  )
-in
-
-let SharedObjectFile =
-  std.contract.from_validator (fun value =>
-    if std.is_string value then
-      if std.string.is_match m%"\.so$"% value then
-        'Ok
+        'Error { message = "not a string" }
+    ),
+  SharedObjectFile =
+    std.contract.from_validator (fun value =>
+      if std.is_string value then
+        if std.string.is_match m%"\.so$"% value then
+          'Ok
+        else
+          'Error { message = "not an .so file" }
       else
-        'Error { message = "not an .so file" }
-    else
-      'Error { message = "not a string" }
-  )
-in
-
-let OptLevel =
-  std.contract.from_predicate (match {
-    0 or 1 or 2 => true,
-    _ => false,
-  }
-  )
+        'Error { message = "not a string" }
+    ),
+  OptLevel =
+    std.contract.from_predicate (match {
+      0 or 1 or 2 => true,
+      _ => false,
+    }
+    ),
 in
 
-let Contract = {
+let GccConf = {
   path_libc
     | doc "Path to libc."
     | Path
@@ -93,9 +88,7 @@ let Contract = {
 }
 in
 
-(
-  {
-    flags = ["Wextra", { flag = "o", arg = "stuff.o" }],
-    optimization_level = 2,
-  } | Contract
-)
+{
+  flags = ["Wextra", { flag = "o", arg = "stuff.o" }],
+  optimization_level = 2,
+} | GccConf
diff --git a/examples/fibonacci/fibonacci.ncl b/examples/fibonacci/fibonacci.ncl
index 26f666b7..c549f256 100644
--- a/examples/fibonacci/fibonacci.ncl
+++ b/examples/fibonacci/fibonacci.ncl
@@ -8,4 +8,5 @@ let rec fibonacci = match {
   n => fibonacci (n - 1) + fibonacci (n - 2),
 }
 in
+
 fibonacci 10
diff --git a/examples/imports/data_nickel_properties.ncl b/examples/imports/data_nickel_properties.ncl
index de8a14b6..360ff8a5 100644
--- a/examples/imports/data_nickel_properties.ncl
+++ b/examples/imports/data_nickel_properties.ncl
@@ -1,12 +1,15 @@
 # test = 'pass'
-let kelvin_to_celcius = fun kelvin => kelvin - 273.15 in
-let kelvin_to_fahrenheit = fun kelvin => (kelvin - 273.15) * 1.8000 + 32.00 in
+let
+  kelvin_to_celcius = fun kelvin => kelvin - 273.15,
+  kelvin_to_fahrenheit = fun kelvin => (kelvin - 273.15) * 1.8000 + 32.00,
+in
 
-let melting_celcius = std.string.from_number (kelvin_to_celcius 1728) in
-let melting_fahrenheit = std.string.from_number (kelvin_to_fahrenheit 1728) in
-
-let boiling_celcius = std.string.from_number (kelvin_to_celcius 3003) in
-let boiling_fahrenheit = std.string.from_number (kelvin_to_fahrenheit 3003) in
+let
+  melting_celcius = std.string.from_number (kelvin_to_celcius 1728),
+  melting_fahrenheit = std.string.from_number (kelvin_to_fahrenheit 1728),
+  boiling_celcius = std.string.from_number (kelvin_to_celcius 3003),
+  boiling_fahrenheit = std.string.from_number (kelvin_to_fahrenheit 3003),
+in
 
 {
   physical = {
diff --git a/examples/imports/imports.ncl b/examples/imports/imports.ncl
index 21e9a81a..1fdcd952 100644
--- a/examples/imports/imports.ncl
+++ b/examples/imports/imports.ncl
@@ -1,21 +1,21 @@
 # test = 'ignore'
 
 # Nickel can import plain yaml, or json files
-let _users = (import "data_users.yml") in
-let _groups = (import "data_groups.json") in
-
-# It even imports toml
-let _machines = (import "data_machines.toml") in
-
-# And of course other nickel files
-let _nickel_properties = (import "data_nickel_properties.ncl") in
+let
+  data_users = (import "data_users.yml"),
+  data_groups = (import "data_groups.json"),
+  # or even toml
+  data_machines = (import "data_machines.toml"),
+  # And of course other nickel files
+  data_nickel_properties = (import "data_nickel_properties.ncl"),
+in
 
 # This is the output object
 {
-  users = _users.users,
-  groups = _groups.groups,
-  machines = _machines.machines,
+  users = data_users.users,
+  groups = data_groups.groups,
+  machines = data_machines.machines,
   off_topic = {
-    nickel_properties = _nickel_properties
+    nickel_properties = data_nickel_properties,
   }
 }
diff --git a/examples/merge-priorities/main.ncl b/examples/merge-priorities/main.ncl
index 15e364a8..d2d7d7f8 100644
--- a/examples/merge-priorities/main.ncl
+++ b/examples/merge-priorities/main.ncl
@@ -2,13 +2,15 @@
 
 # Merge several blocks into one final configuration. In a real world case, one
 # would also want contracts to validate the shape of the data.
-let server = import "server.ncl" in
-let security = import "security.ncl" in
-# Disabling firewall in the final result
+let
+  server = import "server.ncl",
+  security = import "security.ncl",
+in
 server
 & security
 & {
-  # As opposed to the simple merge example, this would now fail
+  # As opposed to the simple merge example, uncommenting the next line would now
+  # fail
   # firewall.enabled = false
 
   firewall.open_ports | priority 10 = [80],
diff --git a/examples/merge/main.ncl b/examples/merge/main.ncl
index 7461c51f..4e2c0b2c 100644
--- a/examples/merge/main.ncl
+++ b/examples/merge/main.ncl
@@ -2,7 +2,9 @@
 
 # Merge several blocks into one final configuration. In a real world case, one
 # would also want contracts to validate the shape of the data.
-let server = import "server.ncl" in
-let security = import "security.ncl" in
+let
+  server = import "server.ncl",
+  security = import "security.ncl",
+in
 # Disabling firewall in the final result
 server & security & { firewall.enabled = false }
diff --git a/examples/record-contract/record-contract.ncl b/examples/record-contract/record-contract.ncl
index a78406cc..31e9f26a 100644
--- a/examples/record-contract/record-contract.ncl
+++ b/examples/record-contract/record-contract.ncl
@@ -8,7 +8,8 @@
 # This example is illustrative. If you actually want to use Nickel with
 # Kubernetes, consider using the auto-generated contracts from
 # https://github.com/tweag/nickel-kubernetes/ instead
-let Port | doc "A contract for a port number"
+let Port
+  | doc "A contract for a port number"
   =
     std.contract.from_predicate (fun value =>
       std.is_number value
@@ -16,14 +17,15 @@ let Port | doc "A contract for a port number"
       && (value >= 0)
       && (value <= 65535)
     )
-  in
+in
 
-let PortElt | doc "A contract for a port element of a Kubernetes configuration"
+let PortElt
+  | doc "A contract for a port element of a Kubernetes configuration"
   = {
     name | String,
     containerPort | Port,
   }
-  in
+in
 
 let Container = {
   name | String,
diff --git a/examples/simple-contracts/simple-contract-bool.ncl b/examples/simple-contracts/simple-contract-bool.ncl
index f739928a..876774ac 100644
--- a/examples/simple-contracts/simple-contract-bool.ncl
+++ b/examples/simple-contracts/simple-contract-bool.ncl
@@ -5,8 +5,10 @@ let EqualsTo = fun reference_value =>
   std.contract.from_predicate ((==) reference_value)
 in
 
-let AlwaysTrue = EqualsTo true in
-let AlwaysFalse = EqualsTo false in
+let
+  AlwaysTrue = EqualsTo true,
+  AlwaysFalse = EqualsTo false,
+in
 
 # This contract says: `not` requires its argument to be true, and in return
 # promise that the return value is false.
diff --git a/examples/simple-contracts/simple-contract-div.ncl b/examples/simple-contracts/simple-contract-div.ncl
index 3777d251..abbb8151 100644
--- a/examples/simple-contracts/simple-contract-div.ncl
+++ b/examples/simple-contracts/simple-contract-div.ncl
@@ -2,16 +2,15 @@
 
 # /!\ THIS EXAMPLE IS EXPECTED TO FAIL
 # Illustrates a basic contract violation.
-let Even =
-  std.contract.from_predicate (fun value =>
-    std.is_number value && value % 2 == 0
-  )
-in
-
-let DivBy3 =
-  std.contract.from_predicate (fun value =>
-    std.is_number value && value % 3 == 0
-  )
+let
+  Even =
+    std.contract.from_predicate (fun value =>
+      std.is_number value && value % 2 == 0
+    ),
+  DivBy3 =
+    std.contract.from_predicate (fun value =>
+      std.is_number value && value % 3 == 0
+    ),
 in
 
 # Will cause an error! 4 is not divisible by 3.


Test patch:
diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap
index 7408ee1e..0be30059 100644
--- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap
+++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap
@@ -4,9 +4,9 @@ expression: err
 ---
 error: contract broken by the caller of `range`
        invalid range
-    ┌─ <stdlib/std.ncl>:769:9
+    ┌─ <stdlib/std.ncl>:771:9
     │
-769 │       | std.contract.unstable.RangeFun Dyn
+771 │       | std.contract.unstable.RangeFun Dyn
     │         ---------------------------------- expected type
     │
     ┌─ [INPUTS_PATH]/errors/array_range_reversed_indices.ncl:3:19
diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap
index ad589a24..65c61f57 100644
--- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap
+++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap
@@ -4,9 +4,9 @@ expression: err
 ---
 error: contract broken by the caller of `range_step`
        invalid range step
-    ┌─ <stdlib/std.ncl>:744:9
+    ┌─ <stdlib/std.ncl>:746:9
     │
-744 │       | std.contract.unstable.RangeFun (std.contract.unstable.RangeStep -> Dyn)
+746 │       | std.contract.unstable.RangeFun (std.contract.unstable.RangeStep -> Dyn)
     │         ----------------------------------------------------------------------- expected type
     │
     ┌─ [INPUTS_PATH]/errors/array_range_step_negative_step.ncl:3:27