On Thu, 23 Jul 2020 at 07:02, Yury Selivanov <yselivanov.ml@gmail.com> wrote:
If you're fascinated by functional programming then your version of
the frozen dict should support efficient (not "O(n)") mutations.

Well, I tried it. No O(n), but the result is quite interesting.

Name: `o.set(key, value)`;      Size:    8; Keys: str; Type: frozendict; Time: 1.43e-07; Sigma: 6e-09
Name: `o.set(key, value)`;      Size:    8; Keys: str; Type:        Map; Time: 1.44e-07; Sigma: 6e-08
////////////////////////////////////////////////////////////////////////////////
Name: `o.set(key, value)`;      Size:    8; Keys: int; Type: frozendict; Time: 1.44e-07; Sigma: 5e-09
Name: `o.set(key, value)`;      Size:    8; Keys: int; Type:        Map; Time: 1.46e-07; Sigma: 6e-08
////////////////////////////////////////////////////////////////////////////////
Name: `o.set(key, value)`;      Size: 1000; Keys: str; Type: frozendict; Time: 8.67e-06; Sigma: 3e-07
Name: `o.set(key, value)`;      Size: 1000; Keys: str; Type:        Map; Time: 2.47e-07; Sigma: 7e-08
////////////////////////////////////////////////////////////////////////////////
Name: `o.set(key, value)`;      Size: 1000; Keys: int; Type: frozendict; Time: 6.21e-06; Sigma: 2e-07
Name: `o.set(key, value)`;      Size: 1000; Keys: int; Type:        Map; Time: 2.89e-07; Sigma: 8e-09

For small dictionaries, it seems that there's almost no difference between imap and fdict. Indeed dict is optimized for size equal or smaller than 8. As expected the difference grows with the size of the dictionary.
About mutate(), in theory fdict could return simply a dict, and dict could have a freeze() method that returns a frozendict. Since dict is quite fast on writing, it's difficult to compare the performances.

This is the source code of the benchmark:
https://github.com/Marco-Sulla/cpython/blob/master/frozendict/test/bench_immutables.py

This is the diff:
https://github.com/python/cpython/compare/24618c5df45dc1096604e7d1dc908359659e660c...Marco-Sulla:master

Maybe immutables.Map can be further optimized. For example, "set()" could use METH_FASTCALL. The only problem is that METH_FASTCALL is not part of the limited API.

I think that a frozenmap is quite interesting, but a frozendict could have the advantage to be fast as dict on reading, and could also have not-so-slow methods to modify itself for common cases. Furthermore, since it has the same struct of dict, it can be accessed by the internal optimized functions.

I also remembered another possible use-case: kwargs in CPython. In C code, kwargs are PyDictObjects. I suppose they are usually not modified; if so, fdict could be used, since it seems to be faster at creation.

On Thu, 23 Jul 2020 at 07:44, Wes Turner <wes.turner@gmail.com> wrote:
pyrsistent.PMap and PRecord may be worth a look

Thank you, I'll check it.