bug icon indicating copy to clipboard operation
bug copied to clipboard

Nothing but frustration inferred

Open scabug opened this issue 14 years ago • 5 comments

You probably would label this as an enhancement rather than a defect, but it bit me really hard in conjunction with #4377.

trait Cap
trait Op[C <: Cap] {
  type Result
}

trait Exec[-C <: Cap, R]

trait ReadCap extends Cap
final class ReadOp[A] extends Op[ReadCap] {
  type Result = A
}

def exec[C <: Cap, O <: Op[C]](op: O): Exec[C, O#Result] = null

exec(new ReadOp[Int])
/*
error: inferred type arguments [Nothing,this.ReadOp[Int]] do not conform to method exec's type parameter bounds [C <: this.Cap,O <: this.Op[C]]
exec(new ReadOp[Int])
^
*/

The first thing that comes to mind is to make Op's C a type member:

trait Cap
trait Op {
  type C <: Cap
  type Result
}

trait Exec[-C <: Cap, R]

trait ReadCap extends Cap
final class ReadOp[A] extends Op {
  type C = ReadCap
  type Result = A
}

def exec[O <: Op](op: O): Exec[O#C, O#Result] = null

exec(new ReadOp[Int])

Which works, until you need to bound Op#C (see #4377):

trait Ops { list =>
  type C <: Cap
  def seq: Seq[Op { type C >: list.C <: Cap }]
}

final class Ops1[O <: Op](op: O) extends Ops {
  type C = O#C
  def seq = Seq[O](op)
}
/*
error: type mismatch;
 found   : Seq[O]
 required: Seq[this.Op{type C >: Ops1.this.C <: this.Cap}]
  def seq = Seq[O](op)
                  ^
*/

My best shot so far is to keep both type parameter and type member, use bounds-friendly [C <: Cap, O <: Op[C]] internally and expose inference-friendly [O <: Op[_ <: Cap]] to the users:

trait Cap
trait Op[C <: Cap] { op =>
  final type CC = C
  type Result
  // Here goes horrid monster
  final type Self[O >: this.type <: Op[_ <: Cap]] =
    Op[O#CC] { type Result = op.Result }
  final def self[O >: this.type <: Op[_ <: Cap]] =
    this.asInstanceOf[Self[O]]
}

trait ReadCap extends Cap
final class ReadOp[A] extends Op[ReadCap] {
  type Result = A
}

trait Ops[C <: Cap] {
  def seq: Seq[Op[C1] forSome { type C1 >: C <: Cap }]
}

final class Ops1[C <: Cap, O <: Op[C]](op: O) extends Ops[C] {
  def seq = Seq[O](op)
}

def inject[O <: Op[_ <: Cap]](op: O) =
  new Ops1[O#CC, O#Self[O]](op.self[O])

inject(new ReadOp[Int])

Frustration achieved with 2.8.1 and 2.10.0.r24524-b20110321020039

scabug avatar Mar 23 '11 06:03 scabug

Imported From: https://issues.scala-lang.org/browse/SI-4378?orig=1 Reporter: Mikhail Vorozhtsov (mikhail.vorozhtsov)

scabug avatar Mar 23 '11 06:03 scabug

Mikhail Vorozhtsov (mikhail.vorozhtsov) said: Note that the naive

def inject[O <: Op[_ <: Cap]](op: O) =
  new Ops1[O#CC, O](op)

does not typecheck.

scabug avatar Mar 23 '11 06:03 scabug

Dotty infers C

  def exec[C <: Cap, O <: Op[C]](op: O): Exec[C, op.Result] = null

  def f = exec(new ReadOp[Int])

som-snytt avatar May 02 '25 22:05 som-snytt

somebody copping Paul's issue naming style

SethTisue avatar May 04 '25 19:05 SethTisue

I had the same thought! but technically, it should read, "Frustration, but Nothing inferred".

som-snytt avatar May 04 '25 19:05 som-snytt