How can you create custom function types in Scala with named parameters?

So, let's say I want to create a custom function type called ImportFunc that takes an Int named fileImportID and a string called filename. I can do this quite easily using an alias like, for example:

type ImportFunc = (Int, String) => Unit 

The problem is that anyone trying to use this function does not know what Int and String are. Is there a way to write something like:

 type ImportFunc = (fileImportID: Int, filename: String) => Unit 
+5
source share
4 answers

When you call a function, you are actually calling the method of applying the function. 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 argument names when making a call with named parameters, this is mandatory in the definition of the apply method. It turns out that in Function2 these arguments are called v1 and v2 . Thus, we can do:

 scala> f.apply(v1=123, v2="file.txt") Importing file #123 (file.txt) 

Now let's see if it works when using 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 not quite the same as fileImportID and filename , but we can fix this with a little refinement of the type:

 type ImportFunc = ((Int, String)=>Unit) { def apply(fileImportID: Int, filename: String): Unit } 

Basically it is just (Int, String)=>Unit (or, in other words, Function2[Int, String, Unit] ), but with an override of apply with our desired argument names. Let's look at this in action:

 scala> val f: ImportFunc = doImport _ f: ImportFunc = <function2> scala> f(fileImportID=123, filename="file.txt") Importing file #123 (file.txt) 

Success!

Important note: in terms of input, ImportFunc identical to Function2[Int, String, Unit] or any other similar refinement. This is because argument names are not part of the signature. Therefore, in my example, f can still be passed anywhere a Function2[Int, String, Unit] (but from now on you can no longer call it using your own argument names).

+3
source

In Scala, functions are defined from FunctionX features, so you can do the following:

 trait ImportFunc extends ((Int, String) => Unit) { def apply(fileImportId: Int, filename: String): Unit } // Then custom definition can be implemented as following val f1: ImportFunc = new ImportFunc { def apply(fid: Int, fn: String): Unit = ??? } f1(1, "name") // call it /** Companion object to ease the use */ object ImportFunc { /** Function factory: take a plain (Int, String) => Unit and turn it into documented type */ def apply(f: (Int, String) => Unit): ImportFunc = new ImportFunc { def apply(fileImportId: Int, filename: String): Unit = f(fileImportId, filename) } } val f2: ImportFunc = ImportFunc((fid: Int, fn: String) => ???) f2(2, "eman") // call it 
+2
source

A simple solution like:

 type FileImportID = Int type Filename = String type ImportFunc = (FileImportID, Filename) => Unit 
+1
source

I don't like Int and String too much, as they mix easily with other Strings and Ints. At:

 case class FileImportID(value: Int) extends AnyVal case class Filename(value: String) extends AnyVal //Leading to type ImportFunc = (FileImportID, Filename) => Unit 
-1
source

Source: https://habr.com/ru/post/1201729/


All Articles