When you call a function, you actually call the function's apply method.
In other words, given this:
def doImport(fileImportID: Int, filename: String) {
println(s"Importing file #$fileImportID ($filename)")
}
The following snippet:
val f = doImport _
f(123, "file.txt")
...is just syntactic sugar for:
val f = doImport _
f.apply(123, "file.txt")
If there is a place where the compiler will look for the arguments's names when doing a call with named parameters, that's necessarily in the apply method's definition.
It turns out that in Function2, those arguments are named v1 and v2. So we can do:
scala> f.apply(v1=123, v2="file.txt")
Importing file #123 (file.txt)
Now let's see if it still works when using the syntactic sugar (in other words when removing the explicit call to apply):
scala> f(v1=123, v2="file.txt")
Importing file #123 (file.txt)
Nice, it works.
Now of course v1 and v2 is not quite the same as fileImportID and filename, but we can fix that with a bit of type refinement:
type ImportFunc = ((Int, String)=>Unit) {
def apply(fileImportID: Int, filename: String): Unit
}
Basically this is just (Int, String)=>Unit (or in other words Function2[Int, String, Unit]) but with a redefinition of apply with our desired argument names.
Let's see this in action:
scala> val f: ImportFunc = doImport _
f: ImportFunc = <function2>
scala> f(fileImportID=123, filename="file.txt")
Importing file #123 (file.txt)
Success!
An important side note: in terms of typing, ImportFunc is identical to Function2[Int, String, Unit], or to any other similar refinement.
This is because argument names are not part of the signature. So in my example f can still be passed anywhere a Function2[Int, String, Unit] is expected
(but from that point you won't be able anymore to call it using your custom argument names).