所以我尝试编写一个扩展视图来增强 PolyFunction:
object PolyFnExtension {
type Base[O] = Function1[Any, Any] {
def apply(x: Any): O
}
extension [O](base: Base[O]) {
def applyOption(x: Any): Option[O] = Some(base.apply(x))
}
val poly = { (x: Any) => x: x.type }
val poly2: [T] => (Seq[T] => Option[T]) = [T] => (x: Seq[T]) => x.headOption
@main def main(): Unit = {
println(Show.showType(poly))
println(Show.showType(poly2))
val r1: Option[Int] = poly.applyOption(1)
println(r1)
val r2: Option[String] = poly.applyOption("abc")
println(r2)
}
}
扩展显然不起作用:
> Task :core:compileScala
[Error] /home/peng/git/dottyspike/core/src/main/scala/com/tribbloids/spike/dotty/PolyFnExtension.scala:27:43: Found: Option[Any]
Required: Option[Int]
Explanation
===========
Tree: com.tribbloids.spike.dotty.PolyFnExtension.applyOption[Any](
com.tribbloids.spike.dotty.PolyFnExtension.poly)(1)
I tried to show that
Option[Any]
conforms to
Option[Int]
but none of the attempts shown below succeeded:
==> Option[Any] <: Option[Int] CachedAppliedType CachedAppliedType
==> Any <: Int CachedTypeRef CachedTypeRef = false
The tests were made under the empty constraint
[Error] /home/peng/git/dottyspike/core/src/main/scala/com/tribbloids/spike/dotty/PolyFnExtension.scala:30:46: Found: Option[Any]
Required: Option[String]
Explanation
===========
Tree: com.tribbloids.spike.dotty.PolyFnExtension.applyOption[Any](
com.tribbloids.spike.dotty.PolyFnExtension.poly)("abc")
I tried to show that
Option[Any]
conforms to
Option[String]
but none of the attempts shown below succeeded:
==> Option[Any] <: Option[String] CachedAppliedType CachedAppliedType
==> Any <: String CachedTypeRef CachedTypeRef = false
The tests were made under the empty constraint
two errors found
在这种情况下,正确的扩展名是什么?
首先,我们需要了解多态函数在 Scala 中是如何工作的,以及它们反编译后的样子:
如下,可以看到一个单态函数及其反编译代码:
// scala
def monoFn(x: Int): Option[Int] = Some(x)
正如我们所见,Scala 通过
int
静态函数将 Integer
类型提升为 .boxToInteger
Object。
// java
public Option<Object> monoFn(int x) {
return (Option<Object>)Some$.MODULE$.apply(BoxesRunTime.boxToInteger(x));
}
我们来写一个多态函数,看看它的反编译代码:
// scala
def polyFn[T](x: T): Option[T] = Some(x)
此时,我们的函数并不将其参数视为原始类型,而是将其视为对象类型。
// java
public <T> Option<T> polyFn(Object x) {
return (Option<T>)Some$.MODULE$.apply(x);
}
当我们调用
polyFn
时,Scala 在运行时将参数转换为 Object
类型。 (例如,int
至 Integer
)
// scala
val pfn: Option[Int] = polyFn(1)
val pfn2: Option[String] = polyFn("hello world")
// java
Option<Integer> pfn = polyFn(BoxesRunTime.boxToInteger(1));
Option<String> pfn2 = polyFn("hello world");
让我们写一个扩展方法并观察如何反编译:
// scala
extension [T](t: T) {
def some: Option[T] = Some(t)
}
@main def main4(): Unit = {
val intSome = 1.some
val strSome = "hello world".some
}
我们可以看到,编译后的扩展方法并没有夸张的实现:
// java
public <T> Option<T> some(Object t) {
return (Option<T>)Some$.MODULE$.apply(t);
}
public void main4() {
Option<Integer> intSome = some(BoxesRunTime.boxToInteger(1));
Option<String> strSome = some("hello world");
}
好吧,让我们回到你的案例。首先我们需要看看我们得到了什么错误:
[error] -- [E007] Type Mismatch Error: /home/csgn/Playground/stackof/src/main/scala/Main.scala:15:42
[error] 15 | val r1: Option[Int] = poly.applyOption(1)
[error] | ^^^^^^^^^^^^^^^^^^^
[error] | Found: Option[Any]
[error] | Required: Option[Int]
[error] |----------------------------------------------------------------------------
[error] | Explanation (enabled by `-explain`)
[error] |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error] |
[error] | Tree: PolyFnExtension.applyOption[Any](PolyFnExtension.poly)(1)
[error] | I tried to show that
[error] | Option[Any]
[error] | conforms to
[error] | Option[Int]
[error] | but none of the attempts shown below succeeded:
[error] |
[error] | ==> Option[Any] <: Option[Int]
[error] | ==> Any <: Int = false
[error] |
[error] | The tests were made under the empty constraint
所以基本上,Scala 说,你不能直接分配给
Option[Int]
因为
编译器无法保证该值是 Int
。因为,Option[Any]
可以包含任何类型。
我们还知道函数(即
polly
)是变量返回类型的决定者。
如下所示,可以看到 polly
返回 Any
类型而不是其参数类型。
// java
public <O> Option<O> applyOption(Function1 base, Object x) {
return (Option<O>)Some$.MODULE$.apply(base.apply(x));
}
static {
poly = (x -> x); // as you can see, there is no type indicated which means it will return any type.
}
public Function1<Object, Object> poly() {
return poly;
}
// and also look, we passing lambda function (i.e., polly) as it is, there is no type explicitly indicated.
Option<?> r1 = applyOption(poly(), BoxesRunTime.boxToInteger(1));
根据反编译的代码,我们知道
poly.applyOption
并没有按照我们想要的方式工作。
// scala
scala> :type poly.applyOption(1)
Any
scala> :type poly.applyOption("hello world")
Any
如果我们希望
polly.applyOption
的结果作为其自己的参数类型,那么我们可以如下实现:
// scala
object PolyFnExtensionFixed {
type Base[T] = Function1[Any, Any] {
def apply(x: Any): T
}
extension [T](base: Base[T]) {
def applyOption[R](x: R): Option[R] = Some(
~~~~^
base.apply(x).asInstanceOf[R]
)
}
}
从现在开始,返回类型可以通过
applyOption
来确定。
// scala
scala> :type poly.applyOption(1)
Option[Int]
scala> :type poly.applyOption("hello world")
Option[String]
你可以在反编译的代码中看到,
base.apply(x)
决定了R
是结果类型。
// java
public <T> Option<R> applyOption(Function1 base, Object x) {
return (Option<R>)Some$.MODULE$.apply(base.apply(x));
}
static {
poly = (x -> x);
poly2 = (x -> x.headOption());
}
public Function1<Object, Object> poly() {
return poly;
}
public Function1 poly2() {
return poly2;
}
public void main2() {
Option r1 = applyOption(poly(), (Function1)BoxesRunTime.boxToInteger(1));
Predef$.MODULE$.println(r1);
Option r2 = applyOption(poly(), (Function1)"abc");
Predef$.MODULE$.println(r2);
}
我希望这是你想要的。