Wednesday, 25 June 2025

Python Tricks 2025

These last years I've already written a couple of posts [1] and [2] putting together some Python tricks that I had been collecting over my development. It's time to put together a few more.

1) I've recently come up with another interesting use case of the walrus operator (aka assignment expression) that I can add to my list here, initializing an object (invoke constructor, dictionary literal) with values that depend ones on others, I mean:


a = 4
b = 8
d1 = {
"f1": (aux := a + b),
"f2": aux + 4 
}

d1
Out[7]: {'f1': 12, 'f2': 16}


A long time ago I posted about some JavaScript techniques for this kind of initialization.

2) Unpacking/destructuring is such a nice Python feature, and while not so rich as its JavaScript counterpart, there are still non trivial cases, like unpacking nested structures.


a, [b, c], d = ["aa", ["bb", "cc"], "dd"]
print(a, b, c, d)
# aa bb cc dd

I recently used it combined with enumerate and zip, which makes a pretty nice example:


names = ['Alice', 'Bob', 'Charlie']
ages = [24, 50, 18]
for i, (name, age) in enumerate(zip(names, ages)):
    print(i, name, age)
# 0 Alice 24
# 1 Bob 50
# 2 Charlie 18

3) When applying a decorator to a function, the decorator function can be any expression, which allows us odd constructs like conditional decorators:


def deco1(fn): return fn

def deco2(fn): return None

do_crash = False

@(deco1 if not crash else deco2)
def do_print(a):
    print(a)
    
do_print("aaa")
# aaa


4) Sometimes you need a "do nothing" function (like a sort of Null Object Pattern, but for functions). The fit it all implementation, that works with any number of positional or keyword arguments, is:


def do_nothing(*ars ,**kwargs):
	pass

5) Immediately Invokable Functions (IIFE's) are quite a thing in JavaScript. In Python we can immediately invoke a lambda expression, but as lambdas can not contain statements, there's no way to immediately invoke a function with statements. Well, there is indeed, using a decorator, a decorator that instead of returning a new function that wraps the decorated one, executes the decorated function There's a good discussion here. For the main use case, Immediately Invoking a function that expects no parameters, you can just use a simple lambda as decorator or use the existing operator.call:



@lambda f: f()
def func():
    print("hey")
hey

@operator.call
def func():
    print("hey")
hey


To also support functions that expects parameters we could use a decorator like this:


invoke = lambda *a, **k: lambda _:_(*a, **k)

@invoke(1, 2, 3)  # works fine :)
def func(a, b, c):
    print(a + b + c) 
6

@invoke()
def func():
    print("hey")   
hey


This is a trick that I don't particularly like and that probably I'll never use, but I've added it here for the sake of knowledge.

Sunday, 8 June 2025

Method Parameter Covariance

I talked about covariance several times in the past, particularly in this post about covariant return types. Since then, C# added support for this feature, and obviously Kotlin comes with it since the beginning. I'll paste below the wikipedia definition just in case:

a covariant return type of a method is one that can be replaced by a "narrower" (derived) type when the method is overridden in a subclass.

Covariant return types (or return type covariance) makes perfect sense, it does not go against any programming principle (Liskov...) and it seems the only reasons for not supporting it were technical limitations in runtimes, compilers...

On the other hand we have Covariant method parameter type. This is a very different story:

To allow the parameters of an overriding method to have a more specific type than the method in the superclass

In principle it seems perfectly logical, as logical as covariant return types, but indeed it's terribly problematic and for the most part not a good idea and that's why mostly no language supports it, save for Eiffel and Dart. The wikipedia article explains it pretty good. As far as I know Python type checkers do not support it either (not even with some optional parameter). I've recently had a real use case at work where I found myself wanting this feature (and not having it), that's why I've been reminded about it and its problems, and I'm talking about it today.

The thing is that such a feature makes perfect sense in terms of modeling the world, but opens the door to runtime errors in our code. If you have an Animal class with an eat(food) method that receives Food, it seems perfectly normal to model a situation where Cat.eat wants to restrict it's food parameter to CatFood. The problem is that then, if we have a variable typed as Animal that is pointing to a Cat, and we provide it Food, rather than CatFood, that will crash at runtime (if our cat is using some CatFood method that is not present in Food). Basically, replacing an Animal by Cat is no longer save, we're going against the Liskov Substitution Principle. To overcome the lack of this feature you can document that your method expects CatFood rather than Food, and use a cast inside it, but as the compiler is not enforcing it, you can go through runtime errors, the difference is that you will be aware of the very specific places where the errors can take place (those where you're doing a casting). I mean (using Kotlin for the example):



// Food hierarchy
open class Food(val name: String)

class CatFood : Food("Cat Food") {
	fun addFishContent(){}
}


open class Animal {
    open fun eat(food: Food) {
        println("Animal eats ${food.name}")
    }
}

class Cat : Animal() {
    override fun eat(food: Food) {
		// I've documented that I should be provided CatFood, not just Food
		// if they've not done I'm fully aware this will CRASH
        (food as CatFood).addFishContent()
    }
}


The above approach seems reasonable, we have a well delimited potential error source (the casting), while error possibilities with "Covariant Parameter type" totally go out of control. Anywhere you use an instance of an "inheritable" class would become a potential error source. Maybe you think you are save because you have not defined any child class with a method with a "covariant parameter types" but anyone could write one in the future, so your code is not safe.

Tuesday, 3 June 2025

JavaScript Builtins Inheritance

In my previous post where I created a class (ResolvablePromise) that inherits from Promise I mentioned this:

I thought that it would be important that the different methods in a ResolvablePromise (then, catch, finally) also returned a ResolvablePromise, rather than a standard Promise, so that we can apply the resolve/reject methods to the promises produced during chaining. I did an implementation and came across an odd problem, caused by the fact that in recent JavaScript versions that's no longer necessary. When subclassing a Promise the then-catch-finally methods of parent Promise already return an instance of the Child class!

That was not like that a few years back. I guess Promises had not been initially designed with the idea of you inheriting from them, so the then, catch, finally methods in the Promise class would always return a Promise, regardless of whether they were being invoked from a child. Well, this is my expected behaviour, I think it's very unlikely that a method (I'm not talking just about JavaScript, but in general) checks if it's being invoked from a derived class (though we've seen that for method chaining it's a really interesting pattern). So, when I created my derived ResolvablePromise I naturally decided to override its then/catch/finally methods to return another ResolvablePromise. This is how I implemented it:


class ResolvablePromise extends Promise {
    constructor(executor) {
	let _resolve, _reject;
	let executorWrapper = (res, rej) => {
	    _resolve = res;
	    _reject = rej;
	    executor(res, rej);
	};      
	super(executorWrapper);
	// now "this" is available
	this.resolve = _resolve;
	this.reject = _reject;  
    }


    then(onFulfilled, onRejected) {
        let pr1 = ResolvablePromise(() => {});
        super.then(onFulfilled, onRejected).then(pr1.resolve, pr1.reject);
        return pr1
    }   

    catch(onRejected) {
        let pr1 = ResolvablePromise(() => {});
        // I have to use .then() here as .catch resolves the promise (unless it throws)
        return super.catch(onRejected).then(pr1.resolve, pr1.reject);
        return pr1;
    }
    
    finally(onFinally) {
        let pr1 = ResolvablePromise(() => {});
        return super.finally(onFinally).then(pr1.resolve, pr1.reject);
        return pr1;
    }

Let's go through the "then" implementation. I create a new ResolvablePromise, that is what I'll end up returning. To execute the normal "then" logic (that will attach the onResolve/onReject handlers to the Promise internals) I invoke the parent "then", and I chain to it the resolution of the new ResolvablePromise. Notice how for "catch" and "finally" I invoke the corresponding parent methods, and then I chain to them again via "then", as the parent "catch" will resolve its promise (unless it decides to throw again), so it's a "then" what I have to chain, not another "catch".

I asked a GPT to review the above code and it came up with some style improvement. It rewrote my "then" method like this:


    then(onFulfilled, onRejected) {
        return ResolvablePromise((res, rej) => {
            super.then(onFulfilled, onRejected).then(res, rej);
        });
    }  

Basically it puts my "invoke parent and chain promise resolution" logic into an executor that it passes to the ResolvablePromise constructor. What was odd to me is that it's using super from an Arrow Function, not from a Child method, so what? I know that Arrow Functions have lexical bindings for "this" and for "arguments", but I had missed that also for super. The arrow function is defined in a Child method, so when it tries to use super it will search for it in the Scope chain, finding it in that Child method. I put below the full, GPT approved, version


 class ResolvablePromise extends Promise {
    constructor(executor) {
        let _resolve, _reject;
        let executorWrapper = (res, rej) => {
            _resolve = res;
            _reject = rej;
            executor(res, rej);
        };      
        super(executorWrapper);
        // now "this" is available
        this.resolve = _resolve;
        this.reject = _reject;  
    }

    then(onFulfilled, onRejected) {
        return ResolvablePromise((res, rej) => {
            super.then(onFulfilled, onRejected).then(res, rej);
        });
    }   

    catch(onRejected) {
        return ResolvablePromise((res, rej) => {
            super.catch(onRejected).then(res, rej);
        });
    }
    
    finally(onFinally) {
        return ResolvablePromise((res, rej) => {
            super.finally(onFinally).then(res, rej);
        });
    }
}

When testing it I was getting an infinite recusion!!!??? After a while I realised that when doing super.then().then() the second then() was being invoked on a ResolvablePromise rather than on Promise, hence the recursion. This felt so odd to me, but investigating a bit I found what I explained at the beginning, that modern JavaScript versions do that in Promises methods, return an instance of the Child class. When asking about this issue to the GPT that had reviewed my code and provided that "style" improvement, he was well aware about this JavaScript evolution (so it would have been nice if he had already told me when reviewing my initial code).

The information from the GPT was very clear, and it also explains that if for some reason we want the old behaviour for our derived class, we can get it by defining a static Symbol.species in our class.

When you call .then() on a promise, the JavaScript engine internally uses the SpeciesConstructor operation to determine what constructor to use for the new promise it returns.
Here's the logic in simplified terms:

- Get the constructor of the current promise:
let C = this.constructor;

- Check for Symbol.species:
let species = C[Symbol.species];

- Use species if defined, otherwise fall back to C:
let resultConstructor = species !== undefined ? species : C;

If you want to check some official documentation, MDN deals with this topic in its subclassing built-ins section. There you can see that this behaviour applies also to the map, filter... methods of Arrays.

If you wonder if for example Kotlin (for the JVM) features something like this, the answer is NO. The map, filter... extension methods that we can apply to Iterables do not check at all the specific Iterable instance that they are being applied on. They return an ArrayList instance (that's an implementation detail, the signature just shows a List interface. From the Kotlin source code:


public inline fun  Iterable.filter(predicate: (T) -> Boolean): List {
    return filterTo(ArrayList(), predicate)
}

public inline fun  Iterable.map(transform: (T) -> R): List {
    return mapTo(ArrayList(collectionSizeOrDefault(10)), transform)
}