如何使用命名参数在Scala中创建自定义函数类型?

时间:2014-09-03 12:51:16

标签: scala lambda

所以我想说我想创建一个名为ImportFunc的自定义函数类型,它接受一个名为fileImportID的Int和一个名为filename的字符串。我可以使用像这样的类型别名轻松地做到这一点:

type ImportFunc = (Int, String) => Unit

问题是,任何试图使用此函数的人都不知道Int和String实际应该是什么。有什么方法可以写出像:

type ImportFunc = (fileImportID: Int, filename: String) => Unit

4 个答案:

答案 0 :(得分:3)

当你调用一个函数时,你实际上调用了函数的apply方法。 换句话说,鉴于此:

def doImport(fileImportID: Int, filename: String) {
  println(s"Importing file #$fileImportID ($filename)")
}

以下代码段:

val f = doImport _
f(123, "file.txt")

...只是语法糖:

val f = doImport _
f.apply(123, "file.txt")

如果在使用命名参数进行调用时编译器将查找参数名称的位置,则必须在apply方法的定义中。 事实证明,在Function2中,这些参数名为v1v2。所以我们可以这样做:

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

现在让我们看看它在使用语法糖时是否仍然有效(换句话说,当删除对apply的显式调用时):

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

很好,很有效。 当然,v1v2fileImportIDfilename并不完全相同,但我们可以通过一些类型细化来解决这个问题:

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

基本上这只是(Int, String)=>Unit(或者换句话说是Function2[Int, String, Unit]),但是使用我们想要的参数名称重新定义apply。 让我们看看这个在行动:

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

成功!

一个重要的旁注:在输入方面,ImportFuncFunction2[Int, String, Unit]相同,或与任何其他类似的改进相同。 这是因为参数名称不是签名的一部分。因此,在我的示例f中仍然可以传递Function2[Int, String, Unit] (但从那时起,你再也无法使用你的自定义参数名称来调用它了。)

答案 1 :(得分:2)

在Scala中,函数是根据特征FunctionX定义的,因此您可以执行以下操作:

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 :(得分:1)

简单&#34;类型&#34;溶液:

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

答案 3 :(得分:-1)

我不太喜欢Int和String,因为它们很容易混淆其他字符串和Ints。做:

case class FileImportID(value: Int) extends AnyVal
case class Filename(value: String) extends AnyVal

//Leading to 
type ImportFunc = (FileImportID, Filename) => Unit