I thought I'd expand my answer on the second option a bit more :-)
A really common thing that we use associative functions for in our team is string manipulation. If we want to lowercase a string (which is a char[]) and then trim it, for example, we might do this:
char myString[] = whatever
char newString[] = stringUtil.trim(stringUtil.lowercase(myString))
...or we can do:
char myString[] = whatever
char newString[] = myString.lowercase().trim()
You do have to understand the associative behavior here to read this code, but for me I think the second version is way more readable.
With the makeString() example, to keep us on track for the starter question here, both IntUtil and DecUtil have a makeString() function. That's OK because they're in different interfaces, but they have different parameter lists: the IntUtil version has an int param, and the DecUtil version has a dec param (plus some optional stuff).
So then we can do this:
component provides App requires io.Output out, data.IntUtil iu, data.DecUtil du {
int App:main(AppParam params[]) {
int i = 17
dec d = 12.9
out.println("my number is $i and another number is $d")
return 0
}
}Or to write the long form of this in the associative way, the compiler is (I think!!) converting the above to:
component provides App requires io.Output out, data.IntUtil iu, data.DecUtil du {
int App:main(AppParam params[]) {
int i = 17
dec d = 12.9
out.println("my number is $(i.makeString()) and another number is $(d.makeString())")
return 0
}
}This is not ambiguous because the compiler knows it's looking for a function with a specific name and a specific first parameter type (int or dec).
Hope that's a bit helpful!