Saturday 31 December 2022

IIEF's

I've been fiddling with Kotlin these last weeks and it's an absolutely amazing language. Honestly I didn't expect it, as it's an static language and I've always considered dynamic languages more appealing to my "Freak inside", but the expressiviness of Kotlin is just incredible. Notice that this expressiviness comes with a price, as there's a learning curve to understand what that beautiful and expressive code is doing. I'm going to use a sample comparing Groovy, JavaScript and Python. Hey, I absolutely love JavaScript and Python, being dynamic languages they still have some rare features that are not possible in Kotlin, but my overall feeling is that at least for non-browser projects, probably I would favor Kotlin for any relatively big project due to its combination of a beautiful language with the amazingly performant JVM.

Same as JavaScript, Kotlin has IIFE's (Immediatelly Invokable Function Expressions). They come very handy when you need a closure, and you'll only have one instance of that closure. In this case, as you won't be invoking that "closure factory" function in several places with different arguments to obtain different instances, but just once, you can just define and invoke the "closure factory" in the place where you first need that instance. As I'm still learning the crazy kotlin syntax, writing the same "one shot" closure factory in Kotlin, JavaScript and Python seemed like a pretty nice exercise to me. Let's see:

This is the Kotlin's most compact code I've come up with. It's beautiful and idiomatic, but I would not say it's easy to read, particularly if you stop coding in Kotlin for some time.


val formatWithCounter = {
    var counter = 0
    { txt: String, wrapper: String -> "${++counter}.${wrapper}${txt}${wrapper}" }
}()


println(formatWithCounter("Paris", "|"))
println(formatWithCounter("Paris", "+"))
// 1.|Paris|
// 2.+Paris+

The JavaScript code is slightly more verbose, but I guess feels a bit clearer. I would say it's the winner for me.


const formatWithCounter = (() => {
    let counter = 0;
    return (txt, wrapper) => `${++counter}.${wrapper}${txt}${wrapper}`
})();


console.log(formatWithCounter("Paris", "|"))
console.log(formatWithCounter("Paris", "+"))
// 1.|Paris|
// 2.+Paris+

The Python code is much more verbose and less elegant. The only Function Expressions in Python are lambdas (that in Python can only contain an expression). We still lack (multi)statement lambdas, as Guido said in 2006 that they were "unpythonic"... which is a reasoning that upsets me quite a bit, and we can not use "def" as an expression. So we have to define the factory function first and invoke it in another line. The same goes for the function being created and returned from the factory, that can not be just a lambda cause in this function I need to add the "nonlocal" line so that the counter variable is trapped by the closure (otherwise, as the variable is being modified, the compiler would consider it as local rather than trap it from the surrounding scope).


def factory():
    counter = 0
    def _format_with_counter(txt, wrapper):
        nonlocal counter
        return f"{(counter:=counter+1)}.{wrapper}{txt}{wrapper}"
    return _format_with_counter   
format_with_counter = factory()


print(format_with_counter("Paris", "|"))
print(format_with_counter("Paris", "+"))
# 1.|Paris|
# 2.+Paris+

An additional note regarding the Python version. I've used the walrus (:=) operator (assignment expressions). These 2 codes are equivalent:


        return f"{(counter:=counter+1)}.{wrapper}{txt}{wrapper}"
        
        # is equivalent to:
        
	counter += 1
        return f"{counter}.{wrapper}{txt}{wrapper}"
        

No comments:

Post a Comment