Saturday 26 March 2022

Self-Referencing function in JavaScript

Long time ago it was possible for a function in JavaScript to reference itself by means of arguments.callee. As you can read here, this is not allowed in strict mode and should be avoided in general. You can read in that same url the explanation of why arguments.callee is no longer considered necessary. OK, I agree for the most part, but I still think that there are situations where a function should be able to reference itself in some way.

The obvious case is when you want to print in your logs the function name without hardcoding that function name (either the string or the "function.name" access). Also, in recursive functions it can be useful to call the function using a self-reference rather than the function name. Let's see an example


function recursivePrint(txt, times){
    if (times > 0){
        console.log(`v1: ${txt}`);
        recursivePrint(txt, --times);
    }
    else{
        console.log("------------");
    }
}

recursivePrint("Francoisi", 3);


p1 = recursivePrint;
p1("Francois", 3);
// v1: Francois
// v1: Francois
// v1: Francois
// ------------



//modify recursivePrint to print v2
recursivePrint = function (txt, times) {
    if (times > 0){
        console.log(`v2: ${txt}`);
        recursivePrint(txt, --times);
    }
    else{
        console.log("------------");
    }

};

p1("Francois", 3);
// v1: Francois
// v2: Francois
// v2: Francois
// ------------


In the above example p1 references to recursivePrint. Then we redefine recursivePrint. p1 continues to point to the initial recursivePrint, but in the recursion, as we are calling the function through the global reference, the ensuing calls use the redefined function. So we have a mix of v1 and v2.

Another use for self-referencing functions is if we want to attach data to the function as properties in the function, and use them from inside the function. It's similar to having a closure, in both cases we have a function with state, the main difference is that in a closure accessing to that state from outside is not possible (unless you use some odd technique like the one that that I describe here). So, I'm talking about this:


function formatter(txt){
    console.log(`v1: ${formatter.pre}txt${formatter.post}`);
}
formatter.pre = "[";
formatter.post = "]";

f = formatter;
f("hi");
//v1: [txt]
formatter = (txt) => {
    console.log(`v2: ${formatter.char}txt${formatter.char}`);
}
formatter.char = "|"

//It's a mess here, we continue to point to the original function, but it tries to get the state from the new function
f("hi");
//v1: undefinedtxtundefined

A solution for me would be having a variable inside the function that points to the function itself. We could use the name self for that variable. We can use closures for that, something like this:


function createSelfReferencingFormatterFunction(){
    let self = function formatter(txt){
        console.log(`my name is: ${self.name}`);
        console.log(`${self.pre}txt${self.post}`);
    };
    return self;
}

let formatter = createSelfReferencingFormatterFunction();
formatter.pre = "[";
formatter.post = "]";
formatter("hi");

// my name is: formatter
// [txt]

That looks pretty nice to me, but having to define a factory function for each function that we want to do self-referencing is not nice. How could we generalize it to have a generic function that receives a function and returns a self-referencing funtion?

I've come up with a solution that leverages the unfamous eval function. You can check this post from some years ago where I explain how eval has access to the scope of the function where it's invoked, in such a way that if we define a new function in the string passed to eval, that new function adds that scope to its scope chain.




function createSelfReferencingFunction(fn){
    //we have to use var, not let
    code = "var self = " + fn.toString() + ";"
    eval(code);
    return self;
}

function formatter2(txt){
    console.log(`my name is: ${self.name}`);
    console.log(`${self.pre}txt${self.post}`);
}

f1 = createSelfReferencingFunction(formatter2);
f1.pre = "[";
f1.post = "]";
f1("hi");

// my name is: formatter2
// [txt] 

Notice the comment in createSelfReferencingFunction where I indicate that let does not work and we have to use var. The reason for this is that eval creates a block, so the scope of let is restricted to that block, while var's scope is the whole function. There's an answer in stackoverflow explaining it.

Because eval introduces a new block of code. The declaration using var will declare a variable outside of this block of code, since var declares a variable in the function scope.

let, on the other hand, declares a variable in a block scope. So, your b variable will only be visible in your eval block. It's not visible in your function's scope.

Sunday 20 March 2022

Python Strictness

I really like JavaScript and Python a lot, both being so profoundly dynamic, and very similar in many aspects, but there are some points where Python is a bit "strict" when compared to JavaScript, and you have to be more careful, let me explain.

Access to non existing items
In JavaScript, access to an index beyond the array length, or to a non existing key in an object or a map, just returns undefined


> let ar = [];
undefined
> ar[2];
undefined

> let ob = {name: "Francois"}
undefined
> ob.city;
undefined


> let m = new Map();
undefined
> m.set("a", "aa");

> m.get("b");
undefined


In python we have to be much more careful, as that kind of access will throw exceptions. So we'll have to check for the array length, the existence of a field via the hasattr() function, and the existence of a dictionary key via "if key in dict" (the has_key() function is deprecated)


>>> ar = []
>>> ar[1]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: list index out of range

>>> d1 = {"name": "Francois"}
>>> d1["city"]
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'city'

>>> if "city" in d1:
...     print("key exists")
... else:
...     print("key does not exist")
key does not exist

>>> class Person:
...     pass
... 
>>> p1 = Person()
>>> p1.name
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'Person' object has no attribute 'name'

>>> hasattr(p1,"name")
False
>>> 


Function parameters/arguments

In JavaScript you can invoke a function with whatever arguments you want, regardless of the parameters defined in the function signature (obviously, the function code will work or not depending on how those arguments are used)


> function fn(a, b){}
undefined
> fn();
undefined
> fn(1,2,3);
undefined


In python you have call the function with a number of arguments that matches the parameters defined in the function (save if you've defined them as *args, **kwargs)


>>> def fn(a,b):
...     pass
... 
>>> fn()
Traceback (most recent call last):
  File "", line 1, in 
TypeError: fn() missing 2 required positional arguments: 'a' and 'b'
>>> fn(1,2,3)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: fn() takes 2 positional arguments but 3 were given
>>> 


strings and null/None
In JavaScript you can print (console.log) a null or undefined value, same as in Python you can print None. But in Python you have to be careful when using "+" for string concatenation (but well, most likely you're using f-strings, right?)


>>> a = "Hi"
>>> b = None

>>> st = a + b
Traceback (most recent call last):
  File "", line 1, in 
TypeError: can only concatenate str (not "NoneType") to str


>>> st = f"{a} - {b}"
>>> st
'Hi - None'

Sunday 6 March 2022

The Reports on Sarah and Saleem

The Israeli-Palestinian conflict is a difficult topic. It's a mess on which my position has fluctuated a bit over the years, but in a greater or lesser degree I've always been quite supportive of normal Israli people that just want to have a state (of course I profoundly distaste orthodox jews and new settlers) and though I have never cared too much about Palestinians (because of Hamas and even worse groups, because of how Palestinian groups were crucial to unleash the bloody Lebanese civil war). That said, I think for sure that Palestinians deserve to have their own state, particularly the ones living in Cisjordania, and I think Israel, as a wealthy country would have to help a normal Palestinian state (one state that should annihilate the Islamist groups) to develop (a way of paying for the land they've taken from them).

Quite often one can find some form of beauty in pain and madness, and we could say that's the case here. All the suffering caused by this conflict has given us some of the best films ever produced: Le fils de l'autre, Capharnaum and Incendies (as I've said, without the Palestinian conflict probably we would have never witnessed the Lebanese Civil war), and now The Reports on Sarah and Saleem.

I've recently watched this excellent film on TVE2. If you watch it you'll probably wonder how could someone come up with such a crazy story, and well, reallity did, as the film seems to be based on real facts.

We have a married Israeli woman (married to an army high rank) and a married Palestinian guy that are having an affair. When you add to this unfrequent (I guess) situation something that could seem harmless, taking a drink together in Bethlehem (Belén), where nobody knows her, you're setting the ground for some crazy developments. In the end the guy ends up accused of seducing her (the wife of a soldier) to gain knowldge about Israeli military actions. The adulterous man becomes an imprisoned hero for Palestinians, and she becomes a traitor, not just to her husband, but to her nation.

It's really good for me to watch films like this, not just because of how instructive and entertaining they are, but because they remind me that not all Palestinians are crazy Hamas islamists. Many of them are normal guys that undergoing a brutally unfair and harsh existence, try to get by, and try to enjoy life, taking a drink, a dance and flirting with girls (event with Israeli ones).

One of the sequences in this film that I think I'll particularly remember in the future is this dialog between the desperate Israli woman and her work colleague, another young, normal, Israeli woman (I mean that she does not look like a religious or nationalist fanatic). I'm not reproducing the original dialog, just the main sense of what they say:

  • It's horrible, I've cheated on my husband
  • Oh, pour girl. Well, it's not that bad, these things sometimes happen
  • But furthermore he (my lover) is a Palestinian
  • What!!?? You are crazy. There are millions of Jewish men in this world and you have an affair with an Arab! How desperate you are!!!

Probably I would have said those same "racists" (yes, "Arabs" are not a race, but you understand) words. I admit that because of the conflict betwen our civilization and that other civilization, sometimes I tend to generalize and consider almost all Arabs, all Maghrebians and all Turks as our enemies. I know this is unfair, I know not all of them are crazy Islamists, crazy nationalists or crazy criminals, but it's easy to generalize, and when I do so, I'm ashamed about it.