Scala 泛型函数与泛型方法

Posted

技术标签:

【中文标题】Scala 泛型函数与泛型方法【英文标题】:Scala generic Function vs Generic Method 【发布时间】:2021-02-21 12:51:24 【问题描述】:

到目前为止,我的印象是在 scala 中定义通用函数的唯一方法是使用方法,例如

def myToString[A](value: A) = println(value)

但是我想出了以下方法:

val myToStringFun: (T forSome type T) => Unit = value => println(value)

有什么我没有看到的,或者是在 Scala 中编写通用函数而不求助于方法的模式?

我以前从未见过这种模式,只是根据我对高等类型和存在主义概念的学习才想出来的......

请分享您对此的想法或智慧......

编辑1:

如果以上是正确的,为什么不使用这种模式,而人们系统地求助于 Method 来实现泛型功能。是否只是为了便于记号

EDIT2:

如果一个函数(T forSome type T) => Unit Any => Unit

看来

val mySeqToString: (Seq[T] forSome type T) => String = 
    case head +: tail => s"$head +: " + seqToString(tail)
    case Nil => "Nil"

等价于

def seqToString[T](seq: Seq[T]): String = seq match 
    case head +: tail => s"$head +: " + seqToString(tail)
    case Nil => "Nil"

正确吗?

【问题讨论】:

这不是一个存在的泛型,在这一点上,这只是 Any 的花哨和令人困惑的语法。 T 在任何意义上都不可用,尝试用它编写一个通用标识函数,或者编写一个接受 TTsList 并返回 布尔值的函数 检查列表是否包含该元素。 有趣,所以写def method[F[_]](e: F[T] forSome type T): String = ???就像是在说e: F[Any]??? Existentials 与 Any 有点不同,特别是因为考虑到对象的变化,它们的行为不同。但是,我个人从来没有发现它们有用(而且,它们总是让我感到惊讶,所以可以说我真的不理解它们) 现有类型也将在 Scala 3 中被删除 Scala 3 将拥有正确的polymorphic functions 【参考方案1】:

在您的函数中键入A 是标准的泛型类型。你可以用它做很多事情,比如请求一个类型类实例:

import cats.implicits._
def myToString[A: cats.Show](value: A) =  println(value.show) 

另一方面,类型(T forSome type T) 是existential type。您可能会通过它更流行的速记符号_(例如List[_])认出它。用那个你无能为力。如果您在

中检查value的类型
val myToStringFun: (T forSome  type T ) => Unit = value => println(value)

您会注意到它是Any。顺便说一句,通过forSome 使用存在类型是being dropped。

函数在 Scala 中是单态的,与多态的方法不同。就我个人而言,我认为this 文章对该主题提供了很好的解释。

【讨论】:

【参考方案2】:

(T forSome type T ) 只是Any,所以(T forSome type T ) => UnitAny => Unit,这是任意A => Unit子类型

通常F[T] forSome type T 不是F[Any](协变FF[+X] 也是如此)。 F[T] forSome type T 又名F[_] 是所有类型F[A](包括F[Any])的超类型。实际上,它是最小的超类型,即所有类型F[A] 中的最小上限(对于固定的F 和任意的A)。

看来

val mySeqToString: (Seq[T] forSome type T) => String = 
  case head +: tail => s"$head +: " + seqToString(tail)
  case Nil => "Nil" 

等价于

def seqToString[T](seq: Seq[T]): String = seq match 
  case head +: tail => s"$head +: " + seqToString(tail)
  case Nil => "Nil" 

正确吗?

没有。

def seqToString[T](seq: Seq[T]): String 是通用的quantification

seqToString: (∀ T) => (seq: Seq[T]) => String

val mySeqToString: (Seq[T] forSome type T) => String 是存在量化

seqToString: ((∃ T), (seq: Seq[T])) => String

在第一种情况下,您可以指定T,您的代码将适用于这个特定的T,例如seqToString[Int] 将接受 Seq[Int]seqToString[String] 将接受 Seq[String] 等。

在第二种情况下你不控制T,该方法接受所有Seq[Int]Seq[String]等。

由于Seq 是协变的,所以Seq[T] forSome type T 只是Seq[Any]

在依赖类型语言中,存在量化 leads 到 Sigma 类型,全称量化导致 Pi 类型。

【讨论】:

所以这将是一个通用函数但是val mySeqToString: (Seq[T] forSome type T) => String = case head +: tail => s"$head +: " + seqToString(tail) case Nil => "Nil" ?? 编辑了我的问题以添加反映您的答案的第二种情况。 但总的来说,我会坚持使用方法来编写我猜是通用的东西:) 非常感谢您对此进行了扩展。准备好后必须回到这个问题:) @MaatDeamon 是的,我正要评论您的第二个示例与第一个示例并没有太大不同。我可以举一个例子,起初它看起来像一个通用的:val genericFun: ((T, List[T]) forSome type T) => Boolean = case (t, list) => list.contains(t) ,快速测试似乎表明它有效:genericFun(3 -> List(1, 3, 5)) 但是,我们可以看到最后它只是 Any 再次使用这个测试:genericFun(3 -> List(1, 0, "")) 同样,主要问题是我们不能在其他参数或返回类型中重用 T【参考方案3】:

Scala 3 (Dotty) 应该提供polymorphic function types,所以类似于多态方法

scala> def myToString[A](value: A) = println(value)
def myToString[A](value: A): Unit

我们可以写多态函数

scala> val myToString: [A] => A => Unit = [A] => (value: A) => println(value)
val myToString: PolyFunctionapply: [A](x$1: A): Unit = <function1>

相当于PolyFunction用多态apply方法精化

scala> val myToString: PolyFunction def apply[A](value: A): Unit = new PolyFunction 
     |   def apply[A](value: A): Unit = println(value)
     | 
val myToString: PolyFunctionapply: [A](value: A): Unit = <function1>

注意不要混淆多态函数类型

[A] => B

具有不同“级别”的 lambda 类型

[A] =>> B

箭头=&gt;&gt; lifts 中的第二个&gt; 到类型级别。

请注意,此功能仍在根据documentation is missing polymorphic functions #7594 进行开发

【讨论】:

以上是关于Scala 泛型函数与泛型方法的主要内容,如果未能解决你的问题,请参考以下文章

作业09-集合与泛型

模板与泛型编程1(函数模板)

为啥将 Collections.emptySet() 与泛型一起使用在赋值中而不是作为方法参数?

模板与泛型编程

二Lua迭代器与泛型for

模板与泛型编程——模板实参推断