-
Notifications
You must be signed in to change notification settings - Fork 9
Support for contextual dot and implicits #395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
christoph-dfinity
commented
Sep 15, 2025
- Adds Self members to enable contextual dot resolution
- Updates List tests to use contextual dot resolution
|
✨ Documentation preview for bd8e69e:
|
Benchmark Resultsbench/ArrayBuilding.bench.mo
|
| 1000 | 100000 | 1000000 | |
|---|---|---|---|
| List | 548_233 |
48_324_535 |
478_161_875 |
| Buffer | 342_005 |
33_903_435 |
339_003_650 |
| pure/List | 302_135 |
30_003_590 |
300_055_972 |
| VarArray ?T | 180_526 |
17_802_956 |
178_003_171 |
| VarArray T | 160_813 |
15_803_243 |
158_003_458 |
| Array (baseline) | 42_695 |
4_003_125 |
40_003_340 |
Heap
| 1000 | 100000 | 1000000 | |
|---|---|---|---|
| List | 272 B |
272 B |
272 B |
| Buffer | 272 B |
272 B |
272 B |
| pure/List | 272 B |
272 B |
272 B |
| VarArray ?T | 272 B |
272 B |
272 B |
| VarArray T | 272 B |
272 B |
272 B |
| Array (baseline) | 272 B |
272 B |
272 B |
Garbage Collection
| 1000 | 100000 | 1000000 | |
|---|---|---|---|
| List | 10.05 KiB |
797.56 KiB |
7.67 MiB |
| Buffer | 8.71 KiB |
782.15 KiB |
7.63 MiB |
| pure/List | 19.95 KiB |
1.91 MiB |
19.07 MiB |
| VarArray ?T | 8.24 KiB |
781.68 KiB |
7.63 MiB |
| VarArray T | 8.23 KiB |
781.67 KiB |
7.63 MiB |
| Array (baseline) | 4.3 KiB |
391.02 KiB |
3.82 MiB |
bench/FromIters.bench.mo $({\color{gray}0\%})$
Benchmarking the fromIter functions
Columns describe the number of elements in the input iter.
Instructions:
Heap:
Stable Memory:
Garbage Collection:
Instructions
| 100 | 10_000 | 100_000 | |
|---|---|---|---|
| Array.fromIter | 48_764 |
4_712_025 |
47_103_135 |
| List.fromIter | 31_698 |
3_061_541 |
30_603_553 |
| List.fromIter . Iter.reverse | 50_297 |
4_832_563 |
48_305_477 |
Heap
| 100 | 10_000 | 100_000 | |
|---|---|---|---|
| Array.fromIter | 272 B |
272 B |
272 B |
| List.fromIter | 272 B |
272 B |
272 B |
| List.fromIter . Iter.reverse | 272 B |
272 B |
272 B |
Garbage Collection
| 100 | 10_000 | 100_000 | |
|---|---|---|---|
| Array.fromIter | 2.76 KiB |
234.79 KiB |
2.29 MiB |
| List.fromIter | 3.51 KiB |
312.88 KiB |
3.05 MiB |
| List.fromIter . Iter.reverse | 5.11 KiB |
469.17 KiB |
4.58 MiB |
bench/ListBufferNewArray.bench.mo $({\color{gray}0\%})$
List vs. Buffer for creating known-size arrays
Performance comparison between List and Buffer for creating a new array.
Instructions:
Heap:
Stable Memory:
Garbage Collection:
Instructions
| 0 (baseline) | 1 | 5 | 10 | 100 (for loop) | |
|---|---|---|---|---|---|
| List | 1_547 |
2_916 |
9_046 |
13_948 |
74_564 |
| pure/List | 1_247 |
1_355 |
2_439 |
3_801 |
31_868 |
| Buffer | 2_119 |
2_271 |
3_518 |
5_085 |
36_640 |
Heap
| 0 (baseline) | 1 | 5 | 10 | 100 (for loop) | |
|---|---|---|---|---|---|
| List | 272 B |
272 B |
272 B |
272 B |
272 B |
| pure/List | 272 B |
272 B |
272 B |
272 B |
272 B |
| Buffer | 272 B |
272 B |
272 B |
272 B |
272 B |
Garbage Collection
| 0 (baseline) | 1 | 5 | 10 | 100 (for loop) | |
|---|---|---|---|---|---|
| List | 576 B |
616 B |
776 B |
884 B |
1.93 KiB |
| pure/List | 360 B |
380 B |
460 B |
560 B |
2.3 KiB |
| Buffer | 856 B |
864 B |
896 B |
936 B |
1.62 KiB |
bench/PriorityQueues.bench.mo $({\color{gray}0\%})$
Different priority queue implementations
_Compare the performance of the following priority queue implementations:
-
PriorityQueue: Binary heap implementation overList. -
PriorityQueueSet: Wrapper overSet<(T, Nat)>._
Instructions:
Heap:
Stable Memory:
Garbage Collection:
Instructions
| A) PriorityQueue | B) PriorityQueueSet | |
|---|---|---|
| 1.) 100000 operations (push:pop = 1:1) | 597_528_283 |
522_729_861 |
| 2.) 100000 operations (push:pop = 2:1) | 742_952_999 |
809_693_415 |
| 3.) 100000 operations (push:pop = 10:1) | 357_911_737 |
873_181_028 |
| 4.) 100000 operations (only push) | 192_422_882 |
886_824_792 |
| 5.) 50000 pushes, then 50000 pops | 776_632_572 |
961_776_534 |
| 6.) 50000 pushes, then 25000 "pop;push"es | 529_475_053 |
922_137_111 |
Heap
| A) PriorityQueue | B) PriorityQueueSet | |
|---|---|---|
| 1.) 100000 operations (push:pop = 1:1) | 272 B |
272 B |
| 2.) 100000 operations (push:pop = 2:1) | 272 B |
272 B |
| 3.) 100000 operations (push:pop = 10:1) | 272 B |
272 B |
| 4.) 100000 operations (only push) | 272 B |
272 B |
| 5.) 50000 pushes, then 50000 pops | 272 B |
272 B |
| 6.) 50000 pushes, then 25000 "pop;push"es | 272 B |
272 B |
Garbage Collection
| A) PriorityQueue | B) PriorityQueueSet | |
|---|---|---|
| 1.) 100000 operations (push:pop = 1:1) | 15.03 MiB |
17.43 MiB |
| 2.) 100000 operations (push:pop = 2:1) | 19.73 MiB |
19.32 MiB |
| 3.) 100000 operations (push:pop = 10:1) | 8.67 MiB |
12.64 MiB |
| 4.) 100000 operations (only push) | 3.87 MiB |
9.96 MiB |
| 5.) 50000 pushes, then 50000 pops | 22.03 MiB |
26.2 MiB |
| 6.) 50000 pushes, then 25000 "pop;push"es | 14.22 MiB |
18.44 MiB |
bench/PureListStackSafety.bench.mo $({\color{gray}0\%})$
List Stack safety
Check stack-safety of the following pure/List-related functions.
Instructions:
Heap:
Stable Memory:
Garbage Collection:
Instructions
| pure/List.split | 24_602_524 |
| pure/List.all | 7_901_014 |
| pure/List.any | 8_001_390 |
| pure/List.map | 23_103_767 |
| pure/List.filter | 21_104_188 |
| pure/List.filterMap | 27_404_742 |
| pure/List.partition | 21_304_994 |
| pure/List.join | 33_105_326 |
| pure/List.flatten | 24_805_667 |
| pure/List.take | 24_605_664 |
| pure/List.drop | 9_904_119 |
| pure/List.foldRight | 19_105_768 |
| pure/List.merge | 31_808_584 |
| pure/List.chunks | 51_510_344 |
| pure/Queue | 142_662_505 |
Heap
| pure/List.split | 272 B |
| pure/List.all | 272 B |
| pure/List.any | 272 B |
| pure/List.map | 272 B |
| pure/List.filter | 272 B |
| pure/List.filterMap | 272 B |
| pure/List.partition | 272 B |
| pure/List.join | 272 B |
| pure/List.flatten | 272 B |
| pure/List.take | 272 B |
| pure/List.drop | 272 B |
| pure/List.foldRight | 272 B |
| pure/List.merge | 272 B |
| pure/List.chunks | 272 B |
| pure/Queue | 272 B |
Garbage Collection
| pure/List.split | 3.05 MiB |
| pure/List.all | 328 B |
| pure/List.any | 328 B |
| pure/List.map | 3.05 MiB |
| pure/List.filter | 3.05 MiB |
| pure/List.filterMap | 3.05 MiB |
| pure/List.partition | 3.05 MiB |
| pure/List.join | 3.05 MiB |
| pure/List.flatten | 3.05 MiB |
| pure/List.take | 3.05 MiB |
| pure/List.drop | 328 B |
| pure/List.foldRight | 1.53 MiB |
| pure/List.merge | 4.58 MiB |
| pure/List.chunks | 7.63 MiB |
| pure/Queue | 18.31 MiB |
bench/Queues.bench.mo $({\color{gray}0\%})$
Different queue implementations
Compare the performance of the following queue implementations:
-
pure/Queue: The default immutable double-ended queue implementation.- Pros: Good amortized performance, meaning that the average cost of operations is low
O(1). - Cons: In worst case, an operation can take
O(size)time rebuilding the queue as demonstrated in thePop front 2 elementsscenario.
- Pros: Good amortized performance, meaning that the average cost of operations is low
-
pure/RealTimeQueue- Pros: Every operation is guaranteed to take at most
O(1)time and space. - Cons: Poor amortized performance: Instruction cost is on average 3x for pop and 8x for push compared to
pure/Queue.
- Pros: Every operation is guaranteed to take at most
- mutable
Queue- Pros: Also
O(1)guarantees with a lower constant factor thanpure/RealTimeQueue. Amortized performance is comparable topure/Queue. - Cons: It is mutable and cannot be used in
sharedtypes (not shareable).
- Pros: Also
Instructions:
Heap:
Stable Memory:
Garbage Collection:
Instructions
| pure/Queue | pure/RealTimeQueue | mutable Queue | |
|---|---|---|---|
| Initialize with 2 elements | 3_092 |
2_304 |
3_040 |
| Push 500 elements | 90_713 |
744_219 |
219_284 |
| Pop front 2 elements | 86_966 |
4_446 |
3_847 |
| Pop 150 front&back | 92_095 |
304_908 |
124_581 |
Heap
| pure/Queue | pure/RealTimeQueue | mutable Queue | |
|---|---|---|---|
| Initialize with 2 elements | 324 B |
300 B |
352 B |
| Push 500 elements | 8.08 KiB |
8.17 KiB |
19.8 KiB |
| Pop front 2 elements | 240 B |
240 B |
192 B |
| Pop 150 front&back | -4.42 KiB |
-492 B |
-11.45 KiB |
Garbage Collection
| pure/Queue | pure/RealTimeQueue | mutable Queue | |
|---|---|---|---|
| Initialize with 2 elements | 508 B |
444 B |
456 B |
| Push 500 elements | 10.1 KiB |
137.84 KiB |
344 B |
| Pop front 2 elements | 12.19 KiB |
528 B |
424 B |
| Pop 150 front&back | 15.61 KiB |
49.66 KiB |
12.1 KiB |
Note: Renamed benchmarks cannot be compared. Refer to the current baseline for manual comparison.
At a high level this allows writing a function call like `List.concat(list1, list2)` as `list1.concat(list2)`, by finding the `List.concat` function in the context using the inferred type of `list1`. This allows for a more fluent style of using code defined with data types and functions as opposed to classes and methods. This implements the design in #2537 with its first two variations. It extends it slightly by not only taking the `Self` type of the module into account, but also checking compatibility with the first argument of the to-be-picked resolution. ### Todo - [x] Decide if the `Self` module type is the way we want to go. I went with it over searching through every function in scope with the hope of keeping compile times in check. - [x] Write up proper spec, by adjusting the design in #2537 to the current implementation - [x] Make a branch of `core` that uses this throughout to see if we like the impact (dfinity/motoko-core#395) - Documentation - [x] Language manual ### Future - Integration with the language server. Don't want to block this PR on this item and I'm not sure what it involves, so I'll defer to the future. - More informal prose with examples (For now just the Language manual has been updated)
* chore: updates matchers package to mops release (#394) * makes comparison arguments to Map functions implicit * use implicits in tests for Map * make comparison arguments to Set functions implicit * update Set tests to use implicits * remove local bindings, now that we can disambiguate Nat and Int * make equal arguments implicit * rename internal Map and Set size to size_ to avoid clash with contextual dot * Implicit pattern across entire `core` package (#398) * First pass * Another pass * Fix stragglers * Fix more stragglers * Apply suggestions from code review Co-authored-by: Christoph <christoph.hegemann@dfinity.org> Co-authored-by: Claudio Russo <claudio@dfinity.org> * Apply suggestions from code review Co-authored-by: Claudio Russo <claudio@dfinity.org> * Various fixes * Apply suggestions from code review Co-authored-by: Claudio Russo <claudio@dfinity.org> * Fix * Update API lockfile --------- Co-authored-by: Christoph <christoph.hegemann@dfinity.org> Co-authored-by: Claudio Russo <claudio@dfinity.org> * rename .size on Queue * add TODO comment * I was using a stale compiler * updates the toolchain to a pre-release * updated mops to pull down draft branch and adjust tests (#399) * feat: Add PriorityQueue (#392) * Partial implementation. * Fix bug and add peek. * Started with tests, and more functions. * Towards more tests, adding SetPriorityQueue. * Completing first implementation, with tests. * Add comments to PriorityQueue.mo * Move set implementation and add benchmark. * Better holes. * Extra benchmark. * More tests. * . * Comments. * Optimization. * Comments for SetWrapper. * Move SetWrapper. * Run format. * . * npm run validate * Fix imports in docstrings. * Add Changelog entry. * Fix Changelog entry. * Remove inefficient push and pop operations (and benchmarks for them). * Move PriorityQueueSet.mo * Fix validation. * Update Changelog. * Update implicits for RealTimeQueue * Update implicits for PriorityQueue * Update API lockfile --------- Co-authored-by: Ryan Vandersmith <ryan.vandersmith@dfinity.org> Co-authored-by: Claudio Russo <claudio@dfinity.org> Co-authored-by: Andrei Constantinescu <andrei.constantinescu@dfinity.org>
|
Closing in favor of #404 |