Skip to content

Allow polymorphic methods in refinements when used as an upper bound #17704

@s5bug

Description

@s5bug

Consider the following:

import scala.reflect.Selectable.reflectiveSelectable

type Mappable[F[_], A] = {
  def map[B](f: A => B): F[B]
}

def doMap[F[x] <: Mappable[F, x], A, B](fa: F[A], f: A => B): F[B] =
  fa.map(f)

The existence of a contract-abiding map method seems to be guaranteed at compile-time.

@Adam-Vandervorst is working on a macroloop project which uses macros to vastly improve the performance of certain collections operations, and such restrictions allow for an easy way to verify that a generated for-comprehension would be valid:

type ForComp[F[_], A] = {
  def map[B](f: A => B): F[B]
  def flatMap[B](f: A => F[B]): F[B]
  def withFilter(f: A => Boolean): F[A]
}

def example[F[x] <: ForComp[F, x], A, B, C](fa: F[A], f: A => F[B], g: B => Boolean, h: B => C): F[C] =
  for {
    a <- fa
    b <- f(a) if g(b)
  } yield h(b)

Note that determining whether a subtyping is valid doesn't require reflection:

> scala
Welcome to Scala 3.1.3 (17.0.3, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> summon[String <:< { def length(): Int }]
val res0: String =:= String = generalized constraint

So my thoughts are that, once we've determined that the subtyping is valid, we should know that the reflective call is valid, right?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions