Sunday 20 August 2023

Returning "nothing"

I've been reviewing a bit how "nullability" and returning "nothing" from functions work in Python, JavaScript and Kotlin.

Python. In Python the null value is represented by the None object. None is an instance (the only instance) of the types.NoneType class, that hence is a singleton.


None.__class__
Out[30]: NoneType

None.__class__.__class__
Out[31]: type

import types

types.NoneType == type(None)
Out[34]: True

None == types.NoneType()
Out[38]: True


Functions without an explicit return statement or with a return without specifying a value, will return None.


# these 3 functions are just the same
def say_hi():
    print("hi")
    

def say_hi_2():
    print("hi")
    return
    

def say_hi_3():
    print("hi")
    return None
    

a = say_hi()
hi

a2 = say_hi_2()
hi

a3 = say_hi_3()
hi

a1 == a2 == a3 == None
a == a2 == a3 == None
Out[46]: True


Javascript. In JavaScript we have 2 different values that we can say mean the absence of value: null and undefined. I could write a whole post about that, so I'll just talk about what this post is about, function returns. A Javascript function without an explicit return statement returns undefined. The same as if you do a return without specifying a value "return;".

Kotlin. In Kotlin functions without a explicit return or with a return without specifying a value, return an instance of Unit. Unit is a singleton class representing a "void return". When declaring a function returning Unit we can skip it in the function signature. The 5 functions below are equivalent, all of them return an instance (the only instance) of Unit


// the "full" way
fun formatAndPrint1(txt: String): Unit {
    println("txt: ${txt}")
    return Unit
}

// the most abbreviated way
fun formatAndPrint2(txt: String) {
    println("txt: ${txt}")
}

// 3 "intermediate" ways
fun formatAndPrint3(txt: String): Unit{
    println("txt: ${txt}")
} 

fun formatAndPrint4(txt: String) {
    println("txt: ${txt}")
    return Unit
}

fun formatAndPrint5(txt: String) {
    println("txt: ${txt}")
    return
}

Kotlin also has the notion of null. The difference between null and Unit is well explained in this discussion:

null and Unit are different things, and meant to be different. null expresses the absence of a value that would usually be there.

fun findByName(name: String): User?

Whereas Unit means essentially “no value”, not even null.

fun delete(item: Item): Unit

There is no useful return value in this case, what is expressed with Unit.

It's a nice distinction that we don't have in Python, where just have None. There's no "equivalence" between Unit and None. A function defined in its signature as returning Unit can not return null, and viceversa, we'll get compiler errors. The 3 functions below cause Compilation errors:


fun sayHi() {
    println("hi")
    return null
}
//compilation error: null can not be a value of a non-null type Unit

fun format2(txt: String): String? {
    if (txt.startsWith("C"))
        return "|${txt}|"
    return Unit
}
// compilation error: type mismatch: inferred type is Unit but String? was expected


fun format3(txt: String): String? {
    if (txt.startsWith("C"))
        return "|${txt}|"
    return 
}
// compilation error: this function must return a value of type String?


Sunday 13 August 2023

Delegation in Kotlin

In my previous post I wrote about how to automate delegation in Python in a way similar to what is featured in Kotlin. As I said delegation is cool, as it favours composition over inheritance, but the examples in the Kotlin documentation do not cover a use case that I think is really common (and it's what makes delegation really useful). So we delegate part of the functionality of one class to another class, but we want that delegated functionality to have access to data in the main class. How would we manage that in Kotlin?

So we have a class to which we want to add a print functionality, we'll do it printable by delegating to a class that implements the Printable interface. Using delegation rather that Interface default method implementations we are able to switch from one Printer implementation to another, that is the beautiful thing of delegation. I've come up with this implementation:


interface Printable {
    var data: Reportable?
    fun print(): String
}

interface Reportable {
    var name: String
    var txt: String
}

class SimplePrinter(val ch: String) : Printable {
    override var data: Reportable? = null
    override fun print(): String = "${ch} ${data?.name}\n ${ch}${ch} ${data?.txt}\n" 
}

class AnotherPrinter(val ch: String) : Printable {
    override var data: Reportable? = null
    override fun print(): String = "${ch}${data?.name}${ch}\n\n${data?.txt}\n" 
}

class Message(override var name: String, override var txt: String, var printer: Printable): Reportable, Printable by printer

fun main(){
    // a Message instance that delegates to SimplePrinter
    val printer1 = SimplePrinter("-")
    val message1 = Message("hello", "this is the main message", printer1)
    printer1.data = message1
    println(message1.print())
    // - hello
    // -- this is the main message



    // a Message instance that delegates to AnotherPrinter
    val printer2 = AnotherPrinter("||")
    val message2 = Message("hello", "this is the main message", printer2)
    printer2.data = message2
    println(message2.print())
    // ||hello||

    // this is the main message

}

In the Printable interface we define a property data that represents the contract (having a "name" and a "txt" properties) of the classes that will delegate calls to Printable (so that Printable can work with that data). So for that data contract we define another interface, Reportable. We have 2 implementations of Printable that can print Reportable objects. Then we define a Message class that implements the Printable interface by delegating to a printer object provided during construction. For that, Message has to implement the Reportable interface, and right after constructing a Message instance, before any call to the delegated method can happen, we have to set the data property of the Printable interface, to point to that Message instance.

The beautiful part of delegation is that our Message class is not coupled to a specific implementation of the Printable interface (as it would happen if we were directly inheriting from one of those implementation). Above, I first create a Message instance that delegates to SimplePrinter, and then a new Message instance that delegates to AnotherPrinter.

The odd thing, is that once the delegation has been set in one object, I can not switch to another implementation. If I try to switch the delegation in my first Message instance, from SimplePrinter to AnotherPrinter, it has no effect, it continues to invoke the methods in SimplePrinter:


    message1.printer = printer2
    println(message1.print())
    // - hello
    // -- this is the main message

If we look into the code generated by the Kotlin compiler we can see that apart from the printer datafield associated to the printer property, the compiler creates a $$delegate_0 data field (that is set also to the printer instance provided in the constructor) ans it's this $$delegate_0 what is used for invoking the delegated methods. So changing later the printer property does not have effect in the delegation, as we continue to use $$delegate_0.

There is this excellent article about Kotlin delegation that also mentions this limitation.

Sunday 6 August 2023

Delegation in Python

Kotlin provides support for delegation at the language level. It's clearly explained in the documentation, so nothing to add, just to remind you that the compiler adds the methods of the interface to the class, methods that just invoke the corresponding method in the object we are delegating to. Delegation is a very nice feature, it follows the "favor composition over inheritance" principle, but I think depending on the use case mixins (either through multiple inheritance and interface default methods or through monkeypatching in languages that allow for that) can be a better approach. Well, that would give for a long reflection...

The thing is that I was thinking about how to implement delegation in Python. One option is using __getattr__, so that calls to a method missing in the object can be delegated to the corresponding method in the object we delegate to. The other option is leveraging python's dynamism to create the "delegator methods" and add them to the class. So basically we do at runtime the same that the Kotlin compiler does. As we are going to transform a class, using a decorator (that decorates a class) is a perfect fit. For each method that we want to delegate we create a function that traps in its closure the name of the attribute to delegate to and the name of the method. We add that function to the class, and that's all.

So let's say we have a sort of "interface" (in Python that means an abc or a Protocol) Formattable and a class TextHelper with an attribute formatter of the FormatterImpl type, and we want to add the methods in Formattable to TextHelper delegating them to formatter. We define the following helper function and decorator:


def _create_method(to_method_name: str, to_attr_name: str):
    # returns a closure that traps the method to invoke and the attribute_name of the object that will act as receiver
    def new_method(self, *args, **kargs):
        # self is an instance of the class FROM which we delegate 
        inner_self = getattr(self, to_attr_name)
        # inner_self is the object TO which we delegate
        to_method = getattr(inner_self, to_method_name) # bound method (to inner_self)
        return to_method(*args, **kargs)
    return new_method


# decorator with parameters, so it has to return a function (that will be invoked with the class being decorated)
# we don't create a new class, we add functions to the existing class and return it
def delegate_interface(interface_like, to_attr_name: str):
    # receives an "interface" for which methods we will create "delegator methods" to delegate from them to the corresponding method in the object indicated by to_attr_name
    def add_methods(cls):
        method_names = [name for name, func in inspect.getmembers(interface_like, predicate=inspect.isfunction) if name != "__init__"]
        for method_name in method_names:
            setattr(cls, method_name, _create_method(method_name, to_attr_name))
        return cls
    return add_methods

That we can use like this:



class Formattable(Protocol):
    @abstractmethod
    def short_format(self, txt: str, prepend: str):
        pass

    @abstractmethod
    def long_format(self, txt: str, wrap: str):
        pass

class FormattableImp(Formattable):
    def short_format(self, txt: str, prepend: str):
        return f"{prepend}{txt}"

    def long_format(self, txt: str, wrap: str):
        return f"{wrap}{txt}{wrap}"

@delegate_interface(Formattable, "formatter")
class TextHelper2:
    def __init__(self, id_, formatter):
        self.id = id_
        self.formatter = formatter

    def beautify(self, txt) -> str:
        return f"beautifying {self}"


helper = TextHelper2("aa", FormattableImp())
print(helper.long_format("hi", "||"))
print(helper.short_format("hi", "||"))

#||hi||
#||hi


Given python's dynamic nature in many occasions we don't make the effort of defining "interfaces" and we could want to delegate a list of methods rather than all the methods in an "interface". I have an additional decorator for that, delegate_methods:



def delegate_methods(method_names: list[str], to_attr_name: str):
    # decorator with parameters   
    # receives a list of method names to create and delegate from them to the corresponding method in the object indicated by to_attr_name
    def add_methods(cls):
        for method_name in method_names:
            setattr(cls, method_name, _create_method(method_name, to_attr_name))
        return cls
    return add_methods

######################################

class Formatter:
    def short_format(self, txt: str, prepend: str):
        return f"{prepend}{txt}"

    def long_format(self, txt: str, wrap: str):
        return f"{wrap}{txt}{wrap}"


@delegate_methods(["short_format", "long_format"], "formatter")
class TextHelper:
    def __init__(self, id_, formatter):
        self.id = id_
        self.formatter = formatter

    def beautify(self, txt) -> str:
        return f"beautifying {self}"

helper = TextHelper("aa", Formatter())
print(helper.long_format("hi", "||"))
print(helper.short_format("hi", "||"))    

#||hi||
#||hi


I've uploaded the code to a gist. After implemeting this I looked around to see what others had done, and came up with the "using decorators" implementation here that is almost identical.