@askonomm @zenforyen @cazabon @kevin @ado

Maybe our code base is not large enoughtπŸ˜‰.

But seriously, I think the reasons are more fundamental.

For example, static type checkers would probably run away in panic from our code base, because we have many situations of indirect function calls manipulating *args.

For example, a cache system based on a #python function based dependency graph, where many function calls are indirect using second order functions and adding parameters dynamically.

@folkerschamel @askonomm @cazabon @kevin @ado I think you're fighting a strawman.

It goes without saying that either you decide to "work with" a type checker, and adopt a #python style to maximize code it can check, or you don't.

Yes, you can't throw pyright or mypy at an arbitrary dynamic wild west code base, and hope it can help you, it can't do magic. So it's a choice you ideally did at the start.

I personally prefer having that additional mechanical pair-programmer in my team.

@zenforyen @askonomm @cazabon @kevin @ado

You can squeeze a round piece into a squared hole. But the solution for a programming problem should reflect the type of problem. Many problems are deeply inherently dynamically typed. I think we have many in our code base.

And when wanting static type checking, then I think it's better to use a static typed language like #java, #cpp or #golang instead of fake static typing aka type hints and misuse #python for something it is not designed for.

@folkerschamel @askonomm @cazabon @kevin @ado If I had the choice, I would not use python for many things.

But there are factors such as library ecosystem and also accessibility. Where I work, still the chance is much higher someone will be able to maintain the type-hinted python code than if I wrote it in a statically typed language.

@zenforyen @askonomm @cazabon @kevin @ado

How would you express something like the following architecture in #mypy friendly #python code or even better in different programming languages like #cpp, #java, #golang etc?

(One part of our software is fundamentally based on countless such calls all over the place.)

def f(smart_evaluator, a, b):
...

def g(...):
x = smart_evaluator.exec(f, someting_extra, a, b)

Of course it is possible, but the question is how natural, easy and elegant it is.

@folkerschamel @zenforyen @askonomm @cazabon @kevin @ado

so f is a parameter to the smart evaluator exec call, and smart evaluator is a parameter to the f call? feels like a complicated setup indicating unclear responsibilities.

It's possible to define precisely the signature of f and smart_evaluator, but i'm not sure it's going to make things a lot clearer.

def f(smart_evaluator: SmartEvaluator, a, b) -> SomeType:
…

class SmartEvaluator:
def exec(f: Callable[[Self, …], SomeType], …):

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

What do you mean by responsibilities? smart_evaluator is not owned by f or g.

Yes, obviously you have static types for the smart evualator, but you loose type checking for a and b, which are the main parameters of all the plenty functions, right?

Btw, some other example:

w = create_wrapper_for_handing_over_to_thread_pool(my_object, ...)

x = await w.some_method_of_my_object(a, b)

How can you statically type check a and b?

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

To clarify: There is one SmartEvaluator, but there are hundreds of functions f having very different parameters.

The intention is making adding and calling new functions f via the smart evaluator as easy as possible.

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

By the way, the #chromium #webrtc implementation implements a kind of create_wrapper_for_handing_over_to_thread_pool in #cpp - horrible!!!

@folkerschamel @zenforyen @askonomm @cazabon @kevin @ado

I mean that i like my dependencies being a DAG, here you have two things referencing each others, and imho usually that means they need to know too much about each others to cooperate.

Having different parameters to all "delegate" functions makes it really hard to type check statically indeed, if at all possible.

Why not add the functions directly to the smart evaluator class, since you need the instance as parameter anyway?

@folkerschamel @zenforyen @askonomm @cazabon @kevin @ado

in the case create_wrapper, it could be a method of te class as well, that returns either a different instance of the class, or setup the instance to be safely called from a thread, and then it's just calling the actual methods of the class, no shenanigans needed to get type checks.

And that's what i mean by unclear responsibilities, if these things are impossible to separate, let them live together in the same class.

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

Wouldn't this mean thatv you have to have to implement every function twice?

@folkerschamel @zenforyen @askonomm @cazabon @kevin @ado

Not sure why? Do you want to be able to call f without an evaluator instance? if so your use case is not really clear to me yet (sorry for being a bit slow). And if so i don't see how that's different in your first example, since it was a parameter.

Maybe a real example that makes sense needs a bit more space than 500 characters. πŸ˜…

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

With calling twice I was referring to the create_wrapper use case. The idea is that you can easily add or change functions of my_object and get the transition into different threads for free.

Yes, that's not the best place for detailed technical discussions.πŸ˜‰

@folkerschamel @zenforyen @askonomm @cazabon @kevin @ado
there are various ways you could handle the thread thing, it depends on your context, what would be unsafe, maybe a decorator to the method would be able to add locks when needed (i.e, only if your instance has asked for thread safety), and run the function in a thread if appropriate, threading changes your signature though (unless you block until the thread completes, to return its result, which kind of defeats the point).

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

Well, I would claim the opposite that it makes our system much simpler than it would be based on a statically typed approach.πŸ˜‰

More in general, these samples are only two of many situations where in my view the problem is naturally dynamically typed. And while it is possible, I would not like to be forced to misuse a statically typed solution.

This is where #python shines.
Many problems can be solved so easily in an elegant compact way.

@tshirtman @zenforyen @askonomm @cazabon @kevin @ado

Enjoy your Sunday too, thanks for the interesting discussion!