What it does is map a function over both arguments of a binary(two-argument) function. This is similar to the B combinator but changed to work on binary functions.
The quintessential usage is when sorting records:
// given a compare functionconst localeCompare =(item1: string,item2: string):number=> item1.localeCompare(item2);// and some accessor function that drills into a data structureconstgetName=(person)=> person.name;// compose that accessor with the compare function to drill both sidesconst compareNames =psi(getName)(localeCompare);// which can be passed to a sort method for an array of that structurepeople.sort(compareNames)
Interestingly, this is equivalent to doing a map and then sort, but using psi is theoretically more memory efficient:
// generates an extra arraypeople.map(getName).sort(localeCompare)
Look out for other opportunities to use psi and I'm sure you'll find them. Particularly if you're doing data aggregation or processing.
Pedantic Disclaimer: Psi is usually defined with the binary function as the first argument but I preferred the similarity to B combinator when taking the mapping function as the first.