Skip to content

Conversation

@christoph-dfinity
Copy link
Contributor

  1. Adds Self members to enable contextual dot resolution
  2. Updates List tests to use contextual dot resolution

@github-actions
Copy link

github-actions bot commented Sep 15, 2025

✨ Documentation preview for bd8e69e:

https://dfinity.github.io/motoko-core/pull/395 (source code)

@github-actions
Copy link

github-actions bot commented Sep 25, 2025

Benchmark Results

bench/ArrayBuilding.bench.mo $({\color{gray}0\%})$

Large known-size array building

Compares performance of different data structures for building arrays of known size.

Instructions: ${\color{gray}0\%}$
Heap: ${\color{gray}0\%}$
Stable Memory: ${\color{gray}0\%}$
Garbage Collection: ${\color{gray}0\%}$

Instructions

1000 100000 1000000
List 548_233 $({\color{gray}0\%})$ 48_324_535 $({\color{gray}0\%})$ 478_161_875 $({\color{gray}0\%})$
Buffer 342_005 $({\color{gray}0\%})$ 33_903_435 $({\color{gray}0\%})$ 339_003_650 $({\color{gray}0\%})$
pure/List 302_135 $({\color{gray}0\%})$ 30_003_590 $({\color{gray}0\%})$ 300_055_972 $({\color{gray}0\%})$
VarArray ?T 180_526 $({\color{gray}0\%})$ 17_802_956 $({\color{gray}0\%})$ 178_003_171 $({\color{gray}0\%})$
VarArray T 160_813 $({\color{gray}0\%})$ 15_803_243 $({\color{gray}0\%})$ 158_003_458 $({\color{gray}0\%})$
Array (baseline) 42_695 $({\color{gray}0\%})$ 4_003_125 $({\color{gray}0\%})$ 40_003_340 $({\color{gray}0\%})$

Heap

1000 100000 1000000
List 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
Buffer 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
pure/List 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
VarArray ?T 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
VarArray T 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
Array (baseline) 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$

Garbage Collection

1000 100000 1000000
List 10.05 KiB $({\color{gray}0\%})$ 797.56 KiB $({\color{gray}0\%})$ 7.67 MiB $({\color{gray}0\%})$
Buffer 8.71 KiB $({\color{gray}0\%})$ 782.15 KiB $({\color{gray}0\%})$ 7.63 MiB $({\color{gray}0\%})$
pure/List 19.95 KiB $({\color{gray}0\%})$ 1.91 MiB $({\color{gray}0\%})$ 19.07 MiB $({\color{gray}0\%})$
VarArray ?T 8.24 KiB $({\color{gray}0\%})$ 781.68 KiB $({\color{gray}0\%})$ 7.63 MiB $({\color{gray}0\%})$
VarArray T 8.23 KiB $({\color{gray}0\%})$ 781.67 KiB $({\color{gray}0\%})$ 7.63 MiB $({\color{gray}0\%})$
Array (baseline) 4.3 KiB $({\color{gray}0\%})$ 391.02 KiB $({\color{gray}0\%})$ 3.82 MiB $({\color{gray}0\%})$
bench/FromIters.bench.mo $({\color{gray}0\%})$

Benchmarking the fromIter functions

Columns describe the number of elements in the input iter.

Instructions: ${\color{gray}0\%}$
Heap: ${\color{gray}0\%}$
Stable Memory: ${\color{gray}0\%}$
Garbage Collection: ${\color{gray}0\%}$

Instructions

100 10_000 100_000
Array.fromIter 48_764 $({\color{gray}0\%})$ 4_712_025 $({\color{gray}0\%})$ 47_103_135 $({\color{gray}0\%})$
List.fromIter 31_698 $({\color{gray}0\%})$ 3_061_541 $({\color{gray}0\%})$ 30_603_553 $({\color{gray}0\%})$
List.fromIter . Iter.reverse 50_297 $({\color{gray}0\%})$ 4_832_563 $({\color{gray}0\%})$ 48_305_477 $({\color{gray}0\%})$

Heap

100 10_000 100_000
Array.fromIter 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
List.fromIter 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
List.fromIter . Iter.reverse 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$

Garbage Collection

100 10_000 100_000
Array.fromIter 2.76 KiB $({\color{gray}0\%})$ 234.79 KiB $({\color{gray}0\%})$ 2.29 MiB $({\color{gray}0\%})$
List.fromIter 3.51 KiB $({\color{gray}0\%})$ 312.88 KiB $({\color{gray}0\%})$ 3.05 MiB $({\color{gray}0\%})$
List.fromIter . Iter.reverse 5.11 KiB $({\color{gray}0\%})$ 469.17 KiB $({\color{gray}0\%})$ 4.58 MiB $({\color{gray}0\%})$
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: ${\color{gray}0\%}$
Heap: ${\color{gray}0\%}$
Stable Memory: ${\color{gray}0\%}$
Garbage Collection: ${\color{gray}0\%}$

Instructions

0 (baseline) 1 5 10 100 (for loop)
List 1_547 $({\color{gray}0\%})$ 2_916 $({\color{gray}0\%})$ 9_046 $({\color{gray}0\%})$ 13_948 $({\color{gray}0\%})$ 74_564 $({\color{gray}0\%})$
pure/List 1_247 $({\color{gray}0\%})$ 1_355 $({\color{gray}0\%})$ 2_439 $({\color{gray}0\%})$ 3_801 $({\color{gray}0\%})$ 31_868 $({\color{gray}0\%})$
Buffer 2_119 $({\color{gray}0\%})$ 2_271 $({\color{gray}0\%})$ 3_518 $({\color{gray}0\%})$ 5_085 $({\color{gray}0\%})$ 36_640 $({\color{gray}0\%})$

Heap

0 (baseline) 1 5 10 100 (for loop)
List 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
pure/List 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
Buffer 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$

Garbage Collection

0 (baseline) 1 5 10 100 (for loop)
List 576 B $({\color{gray}0\%})$ 616 B $({\color{gray}0\%})$ 776 B $({\color{gray}0\%})$ 884 B $({\color{gray}0\%})$ 1.93 KiB $({\color{gray}0\%})$
pure/List 360 B $({\color{gray}0\%})$ 380 B $({\color{gray}0\%})$ 460 B $({\color{gray}0\%})$ 560 B $({\color{gray}0\%})$ 2.3 KiB $({\color{gray}0\%})$
Buffer 856 B $({\color{gray}0\%})$ 864 B $({\color{gray}0\%})$ 896 B $({\color{gray}0\%})$ 936 B $({\color{gray}0\%})$ 1.62 KiB $({\color{gray}0\%})$
bench/PriorityQueues.bench.mo $({\color{gray}0\%})$

Different priority queue implementations

_Compare the performance of the following priority queue implementations:

  • PriorityQueue: Binary heap implementation over List.
  • PriorityQueueSet: Wrapper over Set<(T, Nat)>._

Instructions: ${\color{gray}0\%}$
Heap: ${\color{gray}0\%}$
Stable Memory: ${\color{gray}0\%}$
Garbage Collection: ${\color{gray}0\%}$

Instructions

A) PriorityQueue B) PriorityQueueSet
1.) 100000 operations (push:pop = 1:1) 597_528_283 $({\color{gray}0\%})$ 522_729_861 $({\color{gray}0\%})$
2.) 100000 operations (push:pop = 2:1) 742_952_999 $({\color{gray}0\%})$ 809_693_415 $({\color{gray}0\%})$
3.) 100000 operations (push:pop = 10:1) 357_911_737 $({\color{gray}0\%})$ 873_181_028 $({\color{gray}0\%})$
4.) 100000 operations (only push) 192_422_882 $({\color{gray}0\%})$ 886_824_792 $({\color{gray}0\%})$
5.) 50000 pushes, then 50000 pops 776_632_572 $({\color{gray}0\%})$ 961_776_534 $({\color{gray}0\%})$
6.) 50000 pushes, then 25000 "pop;push"es 529_475_053 $({\color{gray}0\%})$ 922_137_111 $({\color{gray}0\%})$

Heap

A) PriorityQueue B) PriorityQueueSet
1.) 100000 operations (push:pop = 1:1) 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
2.) 100000 operations (push:pop = 2:1) 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
3.) 100000 operations (push:pop = 10:1) 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
4.) 100000 operations (only push) 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
5.) 50000 pushes, then 50000 pops 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$
6.) 50000 pushes, then 25000 "pop;push"es 272 B $({\color{gray}0\%})$ 272 B $({\color{gray}0\%})$

Garbage Collection

A) PriorityQueue B) PriorityQueueSet
1.) 100000 operations (push:pop = 1:1) 15.03 MiB $({\color{gray}0\%})$ 17.43 MiB $({\color{gray}0\%})$
2.) 100000 operations (push:pop = 2:1) 19.73 MiB $({\color{gray}0\%})$ 19.32 MiB $({\color{gray}0\%})$
3.) 100000 operations (push:pop = 10:1) 8.67 MiB $({\color{gray}0\%})$ 12.64 MiB $({\color{gray}0\%})$
4.) 100000 operations (only push) 3.87 MiB $({\color{gray}0\%})$ 9.96 MiB $({\color{gray}0\%})$
5.) 50000 pushes, then 50000 pops 22.03 MiB $({\color{gray}0\%})$ 26.2 MiB $({\color{gray}0\%})$
6.) 50000 pushes, then 25000 "pop;push"es 14.22 MiB $({\color{gray}0\%})$ 18.44 MiB $({\color{gray}0\%})$
bench/PureListStackSafety.bench.mo $({\color{gray}0\%})$

List Stack safety

Check stack-safety of the following pure/List-related functions.

Instructions: ${\color{gray}0\%}$
Heap: ${\color{gray}0\%}$
Stable Memory: ${\color{gray}0\%}$
Garbage Collection: ${\color{gray}0\%}$

Instructions

pure/List.split 24_602_524 $({\color{gray}0\%})$
pure/List.all 7_901_014 $({\color{gray}0\%})$
pure/List.any 8_001_390 $({\color{gray}0\%})$
pure/List.map 23_103_767 $({\color{gray}0\%})$
pure/List.filter 21_104_188 $({\color{gray}0\%})$
pure/List.filterMap 27_404_742 $({\color{gray}0\%})$
pure/List.partition 21_304_994 $({\color{gray}0\%})$
pure/List.join 33_105_326 $({\color{gray}0\%})$
pure/List.flatten 24_805_667 $({\color{gray}0\%})$
pure/List.take 24_605_664 $({\color{gray}0\%})$
pure/List.drop 9_904_119 $({\color{gray}0\%})$
pure/List.foldRight 19_105_768 $({\color{gray}0\%})$
pure/List.merge 31_808_584 $({\color{gray}0\%})$
pure/List.chunks 51_510_344 $({\color{gray}0\%})$
pure/Queue 142_662_505 $({\color{gray}0\%})$

Heap

pure/List.split 272 B $({\color{gray}0\%})$
pure/List.all 272 B $({\color{gray}0\%})$
pure/List.any 272 B $({\color{gray}0\%})$
pure/List.map 272 B $({\color{gray}0\%})$
pure/List.filter 272 B $({\color{gray}0\%})$
pure/List.filterMap 272 B $({\color{gray}0\%})$
pure/List.partition 272 B $({\color{gray}0\%})$
pure/List.join 272 B $({\color{gray}0\%})$
pure/List.flatten 272 B $({\color{gray}0\%})$
pure/List.take 272 B $({\color{gray}0\%})$
pure/List.drop 272 B $({\color{gray}0\%})$
pure/List.foldRight 272 B $({\color{gray}0\%})$
pure/List.merge 272 B $({\color{gray}0\%})$
pure/List.chunks 272 B $({\color{gray}0\%})$
pure/Queue 272 B $({\color{gray}0\%})$

Garbage Collection

pure/List.split 3.05 MiB $({\color{gray}0\%})$
pure/List.all 328 B $({\color{gray}0\%})$
pure/List.any 328 B $({\color{gray}0\%})$
pure/List.map 3.05 MiB $({\color{gray}0\%})$
pure/List.filter 3.05 MiB $({\color{gray}0\%})$
pure/List.filterMap 3.05 MiB $({\color{gray}0\%})$
pure/List.partition 3.05 MiB $({\color{gray}0\%})$
pure/List.join 3.05 MiB $({\color{gray}0\%})$
pure/List.flatten 3.05 MiB $({\color{gray}0\%})$
pure/List.take 3.05 MiB $({\color{gray}0\%})$
pure/List.drop 328 B $({\color{gray}0\%})$
pure/List.foldRight 1.53 MiB $({\color{gray}0\%})$
pure/List.merge 4.58 MiB $({\color{gray}0\%})$
pure/List.chunks 7.63 MiB $({\color{gray}0\%})$
pure/Queue 18.31 MiB $({\color{gray}0\%})$
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 the Pop front 2 elements scenario.
  • 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.
  • mutable Queue
    • Pros: Also O(1) guarantees with a lower constant factor than pure/RealTimeQueue. Amortized performance is comparable to pure/Queue.
    • Cons: It is mutable and cannot be used in shared types (not shareable).

Instructions: ${\color{gray}0\%}$
Heap: ${\color{gray}0\%}$
Stable Memory: ${\color{gray}0\%}$
Garbage Collection: ${\color{gray}0\%}$

Instructions

pure/Queue pure/RealTimeQueue mutable Queue
Initialize with 2 elements 3_092 $({\color{gray}0\%})$ 2_304 $({\color{gray}0\%})$ 3_040 $({\color{gray}0\%})$
Push 500 elements 90_713 $({\color{gray}0\%})$ 744_219 $({\color{gray}0\%})$ 219_284 $({\color{gray}0\%})$
Pop front 2 elements 86_966 $({\color{gray}0\%})$ 4_446 $({\color{gray}0\%})$ 3_847 $({\color{gray}0\%})$
Pop 150 front&back 92_095 $({\color{gray}0\%})$ 304_908 $({\color{gray}0\%})$ 124_581 $({\color{gray}0\%})$

Heap

pure/Queue pure/RealTimeQueue mutable Queue
Initialize with 2 elements 324 B $({\color{gray}0\%})$ 300 B $({\color{gray}0\%})$ 352 B $({\color{gray}0\%})$
Push 500 elements 8.08 KiB $({\color{gray}0\%})$ 8.17 KiB $({\color{gray}0\%})$ 19.8 KiB $({\color{gray}0\%})$
Pop front 2 elements 240 B $({\color{gray}0\%})$ 240 B $({\color{gray}0\%})$ 192 B $({\color{gray}0\%})$
Pop 150 front&back -4.42 KiB $({\color{gray}0\%})$ -492 B $({\color{gray}0\%})$ -11.45 KiB $({\color{gray}0\%})$

Garbage Collection

pure/Queue pure/RealTimeQueue mutable Queue
Initialize with 2 elements 508 B $({\color{gray}0\%})$ 444 B $({\color{gray}0\%})$ 456 B $({\color{gray}0\%})$
Push 500 elements 10.1 KiB $({\color{gray}0\%})$ 137.84 KiB $({\color{gray}0\%})$ 344 B $({\color{gray}0\%})$
Pop front 2 elements 12.19 KiB $({\color{gray}0\%})$ 528 B $({\color{gray}0\%})$ 424 B $({\color{gray}0\%})$
Pop 150 front&back 15.61 KiB $({\color{gray}0\%})$ 49.66 KiB $({\color{gray}0\%})$ 12.1 KiB $({\color{gray}0\%})$

Note: Renamed benchmarks cannot be compared. Refer to the current baseline for manual comparison.

mergify bot pushed a commit to dfinity/motoko that referenced this pull request Sep 26, 2025
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)
christoph-dfinity and others added 3 commits September 27, 2025 19:57
* 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>
@christoph-dfinity christoph-dfinity changed the title experiment: Contextual dot resolution Support for contextual dot and implicits Oct 13, 2025
@christoph-dfinity
Copy link
Contributor Author

Closing in favor of #404

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant