Relative scoping for hierarchical ADT arguments

OK, so going back to the original example: An api-side export takes us to a bit less verbosity:

// This is real Scala 3.7.4
final case class Shape(geometry : Shape.Geometry, color : Shape.Color) 
object Shape:
  export Geometry.*, Color.*

  sealed trait Geometry
  object Geometry:
    case object Triangle extends Geometry
    case object Rectangle extends Geometry
    case object Circle extends Geometry
  end Geometry

  sealed trait Color
  object Color:
    case object Red extends Color
    case object Green extends Color
    case object Blue extends Color
  end Color
end Shape

You then can:

val redCircle = Shape(Shape.Circle, Shape.Red)

But we would like to:

val redCircle = Shape(Circle, Red)

The above could be achieved with further exports on top level of the package but then the namespace outside of Shape is “polluted”.

A proposal is that we could achieve this by allowing export in new positions to unlock target-type scope injection:

// this is hypothetical Scala
final case class Shape(geometry : Shape.Geometry, color : Shape.Color) 
object Shape:
  sealed trait Geometry
  object Geometry:
    export case object Triangle extends Geometry
    export case object Rectangle extends Geometry
    export case object Circle extends Geometry
  end Geometry

  sealed trait Color
  object Color:
    export case object Red extends Color
    export case object Green extends Color
    export case object Blue extends Color
  end Color
end Shape

The name space is not “polluted” and target-typing-based member selection works outside Shape where Shape stuff is expected like so and the api-designer can make a nice api:

val g: Shape.Geometry = Circle

This possibility would be discoverable in the api-docs by the export modifier. IDE-support could show hints and make completion etc.

@soronpo What do you think about that? Would it solve your original use case?

1 Like