openlang

How can I subclass a class inside another class?

Hi,

In a package whose code is outside my control, there is a hierarchy of classes arranged as follows:

class Outer:
    class Inner(number: Int):
        ???

I'm trying to add a wrapper around Inner as a convenience, but because Inner depends on its enclosing instance, I've only managed to place it within a subclass of that enclosing class:

new Outer:
    case class InnerCase(number: Int) extends Inner(number):
        ???

    (new InnerCase(3)).copy() // sample usage

Unfortunately, subclassing Outer in several places means repeating that convenience case class inside each new subclass. To avoid repeatedly duplicating the definition, I'd prefer a class usable with an arbitrary Outer#Inner This is the solution I tried:

case class InnerCase(number: Int)(owner: Outer) extends owner.Inner(number):
    ???

new Outer:
    host =>
    InnerCase(3)(host).copy()(host)

new Outer:
    host =>
    val instance = InnerCase(4)(host).copy()(host)
    val widened: Inner = instance // line causing trouble

But using an InnerCase polymorphically in a place requiring an Inner produces this diagnostic:

Found: InnerCase
Required: Inner

I'm still very much a beginner with Scala, so I imagine there must be a better way to achieve what I'm after here. Presumably there's a type-theoretic explanation for why my direct approach violates the type system. Either way, I'd really welcome pointers toward the “proper” approach to subclassing an inner class.

The conversation continues 1

Follow the reasoning
  1. Permalink

    case class InnerCase(number: Int)(owner: Outer) extends owner.Inner(number): ???

    That won't compile at all in current Scala 3. Referring to a constructor's parameter from its parent type is unsound. The Scala 3 compiler tracker explains this under issue 16270 about constructor parameters appearing unsafely in parent types. Problems further down the line with older compilers (including what you're seeing) frequently follow from that unsoundness.

    Try arranging the code like this:

    class Outer:
        class Inner(number: Int):
            ???
    
    final class Holder[O <: Outer](val owner: O):
        case class InnerCase(number: Int) extends owner.Inner(number):
            ???
    
    new Outer:
        host =>
        Holder[this.type](this).InnerCase(3).copy()
    
    new Outer:
        host =>
        val holder: Holder[this.type] = Holder(this)
        val instance = holder.InnerCase(4).copy()
        val widened: Inner = instance // works here

Other sides of the same question

  1. Comparing have with let bindings in Lean4

    I've observed that these two expressions and behave identically. This also happens in theorem proofs. Are they interchangeabl…

    Replies 3Latest activity lean-4
  2. TypeScript Map initialization with string array entries

    I'm creating a Map whose entries each hold an array of strings. How can I declare its type and initialize it together in one…

    Replies 2Latest activity typescript
  3. Typescript Callable Contract

    Why does Typescript accept my function definition even though it differs from the interface signature, yet report a problem w…

    Replies 2Latest activity typescript