admin管理员组

文章数量:1333168

So I tried to write an extension view to augment a 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)
  }
}

The extension apparently doesn't work:


> 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

What's the proper extension in this case?

UPDATE 1: I forgot to add the goal of this question, so here it is:


  {
    val poly = { (x: Any) => x: x.type }
    val r1: Option[Int] = poly.applyOption(1)
  }

  {
    val poly = { (x: Any) => Seq(x: x.type) }
    val r1: Option[Seq[Int]] = poly.applyOption(1)
  }

It should be noted that we don't know what kind of dependent/poly function will be provided, but the 1 extension should work on all of them

So I tried to write an extension view to augment a 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)
  }
}

The extension apparently doesn't work:


> 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

What's the proper extension in this case?

UPDATE 1: I forgot to add the goal of this question, so here it is:


  {
    val poly = { (x: Any) => x: x.type }
    val r1: Option[Int] = poly.applyOption(1)
  }

  {
    val poly = { (x: Any) => Seq(x: x.type) }
    val r1: Option[Seq[Int]] = poly.applyOption(1)
  }

It should be noted that we don't know what kind of dependent/poly function will be provided, but the 1 extension should work on all of them

Share Improve this question edited Nov 29, 2024 at 20:31 tribbloid asked Nov 28, 2024 at 3:56 tribbloidtribbloid 3,76415 gold badges70 silver badges116 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

First of all, we need to understand how polymorphic functions work in Scala and how they look like when decompiled:

As follows, you can see a monomorphic function and its decompiled code:

// scala
def monoFn(x: Int): Option[Int] = Some(x)

As we can see, Scala lifts the int type into Integer Object via .boxToInteger static function.

// java
public Option<Object> monoFn(int x) {
    return (Option<Object>)Some$.MODULE$.apply(BoxesRunTime.boxToInteger(x));
}

Lets write a polymorphic function and look at its decompiled code:

// scala
def polyFn[T](x: T): Option[T] = Some(x)

At this time, our function doesn't take its argument as primitive type, but as Object type.

// java
public <T> Option<T> polyFn(Object x) {
    return (Option<T>)Some$.MODULE$.apply(x);
}

When we call the polyFn, Scala converts the parameters into Object types in runtime. (e.g., int to 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");

Lets write an extension method and observe how decompiled:

// scala
extension [T](t: T) {
    def some: Option[T] = Some(t)
}

@main def main4(): Unit = {
    val intSome = 1.some
    val strSome = "hello world".some
}

As we can see, there is no exaggerated implementation in compiled the extension method:

// 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");
}

Okay lets back on your case. First we need to look what error we got:

[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

So basically, Scala says, you can't directly assign to Option[Int] because the compiler cannot guarantee that the value is an Int. Since, Option[Any] can contain any type.

And also we know that the function (i.e., polly) is the determiner of the return type of the variable. As follows, you can see polly returns Any type not its parameter type.

// 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));

In the light of the decompiled code, we know that the poly.applyOption does not works the way we want it to.

// scala
scala> :type poly.applyOption(1)
Any

scala> :type poly.applyOption("hello world")
Any

If we want the polly.applyOption's result as its own parameter type then we can implement as follows:

// 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]
    )
  }
}

From now on, return type can determined by applyOption.

// scala
scala> :type poly.applyOption(1)
Option[Int]

scala> :type poly.applyOption("hello world")
Option[String]

You can see on decompiled code, base.apply(x) determines the R which is result type.

// 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);
}

I hope this is what you want.

本文标签: In Scala 3how to write extension for a dependent or polymorphic functionStack Overflow