Hi Bruno,

On 4/15/24 11:24 AM, Bruno Haible wrote:
> It's better to not do it, and keep the code as-is.

That is fine with me. Though I will offer my perspective on some of
your reasonings. Similar considerations to a previous thread [1].

>   * As I already mentioned, it's good to keep the code understandable,
>     without going "all-in" on all possible features that the programming
>     language offers.
> 
>     I'm all for simplifications that keep us at the same level of
>     abstraction (such as using 'open' instead of 'codecs.open'). But here,
>     we would be starting to use the meta-level ("higher-order functions").
>     This can be useful in some cases, e.g. Java JUnit makes good use of it
>     in order to avoid repetitive code. But it's an extra steep piece of
>     learning curve. For saving 9x 3 lines of code? Not worth it.

I don't necessarily disagree with your point about the use of
higher-order functions. I found decorator functions really confusing
when I first started using Python, and try to avoid writing them for
the most part [2].

I make an exception to this rule for decorators that are a part of the
Python language or standard library. It is sort of hard to avoid them,
in my opinion. As an example, all (I think?) of the functions in the
GLInfo class can be static. I haven't used Java in a long time, but
I think you would write this as:

     public class GLInfo {
         public static String license () { ... }
     }

vs in Python:

    class GLInfo:
        @staticmethod
        def license(self) -> str:
            ...

Not really suggesting this change, but just as an example of how the
higher-order functions are necessary in some situations.

Another example is using 'classmethod' to define alternate
constructors [3]. In Java you would typically just overload the
constructor but in Python you can't do that.

The value I saw was less in the lines removed and more in the level of
nesting removed. I think this make things more readable, but also deep
nesting causes actual issues. For example the break in
GLModuleTable.add_dummy() doesn't properly break the loop over all of
the modules [4]. In shell we use 'break [n]' but Python doesn't
support this since "Flat is better than nested." [5].

Instead of checking the flag at the end of each loop or making their
conditions more complex, the correct solution there is probably just
to flatten them out.

>   * It would introduce hidden global state, that is a constraint for
>     future developments. Namely, if in the future we would need a GLModule
>     to become mutable (not sure this would be a good decision, but anyway),
>     then these caches behind the scenes would become a big surprise. It's
>     better if it is visible in plain sight, not hidden.

Interesting point that I didn't think about. For what its worth it is
pretty easy to clear the cache.

Here is an example program:

-------------------------------
#!/usr/bin/env python3

from functools import lru_cache

class Example:
    def __init__(self) -> None:
        pass

    @lru_cache(maxsize=None)
    def fibonacci(self, value: int) -> int:
        if value <= 2:
            result = 1
        else:
            result = self.fibonacci(value - 1) + self.fibonacci(value - 2)
        return result

var = Example()
print(var.fibonacci.cache_info())
var.fibonacci(300)
print(var.fibonacci.cache_info())
var.fibonacci.cache_clear()
print(var.fibonacci.cache_info())
-------------------------------

Output:

    CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)
    CacheInfo(hits=297, misses=300, maxsize=None, currsize=300)
    CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)

[1] https://lists.gnu.org/archive/html/bug-gnulib/2024-03/msg00361.html
[2] https://docs.python.org/3/glossary.html#term-decorator
[3] https://docs.python.org/3/library/functions.html#classmethod
[4] 
https://git.savannah.gnu.org/cgit/gnulib.git/tree/pygnulib/GLModuleSystem.py#n1019
[5] https://peps.python.org/pep-0020/#the-zen-of-python

Collin

Reply via email to