// The content of Option is some unknown type, an existential type
val option: Option[?] = ...
// This method is defined with generic parameter A
def asString[A]: A => String = _.toString
// Here, compiler applies unknown type to asString
// - we don't know it but since asString can be applied
// FOR EVERY TYPE it must be applicable to our unknown type as well.
option.map(asString)
个字符 在Scala 3中,存在类型的使用是有限的,但你可以使用路径依赖类型来表达这个想法:
trait MyType {
type MyAbstractType
val value: MyAbstractType
val function: (MyAbstractType, MyAbstractType) => MyAbstractType
}
// We have no idea what myType.MyAbstractType is...
val myType: MyType = ...
// ...but we can pass values around when we know what this unknown type
// match the type of method arguments etc.
myType.function(myType.value, myType.value)
object Utility {
class Helper[Input] {
def apply[Output](f: Input => Output): Input => Output = f
}
def fixInputInferOutput[Input] = new Helper[Input]
}
import scala.reflect.runtime.universe.*
import scala.tools.reflect.*
trait SomeSequence { self =>
type ElementType
val ElementType: String
val sequence: List[ElementType]
def parseLambdaAndMap(lambda: String): SomeSequence = {
val code = s""" Utility.fixInputInferOutput[$ElementType]($lambda) """
val toolbox = runtimeMirror(getClass.getClassLoader).mkToolBox()
val tree = toolbox.parse(code)
new SomeSequence {
// The type isn't necessarily Any, but it's easier to implement it that
// way - the important part is that outside world would have to rely
// on the ElementType string when chaining the results
type ElementType = Any
val ElementType = tree.tpe.finalResultType.typeSymbol.fullName
val sequence = self.sequence.map(
toolbox.compile(tree)().asInstanceOf[ElementType => Any]
)
}
}
}
object SomeSequence {
def apply[A: WeakTypeTag](list: List[A]): SomeSequence = new SomeSequence {
type ElementType = A
val ElementType = weakTypeTag[A].toString()
val sequence = list
}
}
1条答案
按热度按时间cngwdvgl1#
您不能在类型系统中表示您不知道的类型,因为它在运行时会变化。你最多只能表达你知道它存在的知识,以及某样东西将与它属于同一类型。
这就是泛型和存在类型的作用。
泛型是表示 * 全称量化 * 的类型系统方式:对于每一个类型X,都有一些东西跟随。
def method[A >: L <: U](a: A) = ...
是构造的一个证明,即在没有任何假设的情况下,除了上下限和一个值之外,物体可以构造为A
而不知道A
。在method
里面,你只知道A
的存在,而不知道别的!在外部,您可以指定它为您想要的任何内容,并获得具体的实现。存在类型是这样一种类型,您知道该类型存在,正是因为您收到了它的示例。例如
def method(opt: Option[?]) = ...
。它是 * 存在量化 * 的类型系统编码。它与泛型的不同之处在于您没有此未知类型的句柄。在实践中,如果你在name后面看到
[A, B, ...]
,它将是泛型的,如果有?
(在Scala 3中,在Scala 2中没有-Xsource:3
和其他标志,存在类型有_
,很容易与类型构造函数混淆)。但你常常可以把一个翻译成另一个:个字符
在Scala 3中,存在类型的使用是有限的,但你可以使用路径依赖类型来表达这个想法:
型
这让我们回到你的问题,电流和the previous one。
你想创建类似于:
型
这里的问题是你不能编译没有输入类型(
a => something
)的lambda表达式。你最多可以对初始输入类型(你知道的)建模,但其余的类型是在运行时创建的,所以你不能静态地表达它们。然而,你可以用存在类型/路径依赖类型来建模你的代码。一个草案,应该提出的一般想法(但不一定工作)可能看起来像:
型
用作
型
当编写这样的东西时,你就进入了REPL、类似Scastie的ID、编译器的领域,可能还有Apache Spark的lambda序列化。在这种情况下,我建议你学习一些关于类型理论、编译器、运行时和编译时反射的课程--以便对你正在尝试做的事情有一个很好的理解。这并不简单,它不可能在一个晚上被破解(除非你真的知道你在做什么),如果不是出于教育目的,我建议尝试使用一些现有的工作,因为从现在开始它只会更难。