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
在任何意义上都不可用,尝试用它编写一个通用标识函数,或者编写一个接受 T
和 Ts
的 List 并返回 布尔值的函数 检查列表是否包含该元素。
有趣,所以写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 ) => Unit
是Any => Unit
,这是任意A => Unit
的子类型。
通常F[T] forSome type T
不是F[Any]
(协变F
即F[+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
箭头=>>
lifts 中的第二个>
到类型级别。
请注意,此功能仍在根据documentation is missing polymorphic functions #7594 进行开发
【讨论】:
以上是关于Scala 泛型函数与泛型方法的主要内容,如果未能解决你的问题,请参考以下文章