在Scala 3中,如何为依赖或多态函数编写扩展?

问题描述 投票:0回答:1

所以我尝试编写一个扩展视图来增强 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 extension-methods dependent-type scala-3
1个回答
0
投票

首先,我们需要了解多态函数在 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);
}

我希望这是你想要的。

© www.soinside.com 2019 - 2024. All rights reserved.