Extension types

Is it possible to expand upon the extension methods concept and have extension types:

trait Foo
trait Bar[T]
extension [F <: Foo](f : F){
  type Extended = Bar[F]
}
trait SpecificFoo[T] extends Foo
val f = new SpecificFoo[5]{}
summon[f.Extended =:= Bar[SpecificFoo[5]]]
3 Likes

What would such types expand to? Extension methods are just normal methods. But if I apply their translation to extension types I would get

type Extended(f: F)

That’s not valid type syntax, or semantics. Types cannot take values as parameters. Maybe they should, that’s a good area to work on which gets us more on the road to dependent types. But that work needs to come first. Afterwards we can dream about type extensions.

No, it should add a type member to F which is a normal extension, if I understand that correctly

Can you say precisely what you mean by that?

Your message talks about extension type Extended(f: F) or something like that which is out of scope for the question which is asking for extension [Typ](t: Typ) type ListOf = List[Typ] which is just a type member for Typs eg type IntList = Int.ListOf

I think at least

I meant: what do you mean by

Can be specific? What would be the rule for expanding types? We’d need examples and then a general rule that works for the examples.

Huh? The same way

extension [A] (a: A)
  def k = () => a

adds a method k to As,

extension [A] (a: A)
  type K = () => A

adds a type member (is that what you call them?) Potentially an extension that only has types can be written as

extension [A]
  type K = () => A

without requiring an instance

No, you are still on the surface. Have a look at the definition of extension methods. Extension Methods. It contains a section “translation” at the very top. We need something analogous for extension types.

extension [A] type Hello[B, C] = A | Either[B, C] = <extension> type Hello[A, B, C] = A | Either[B, C]

That would work. But the original version had a value parameter:

extension [F <: Foo](f : F){
  type Extended = Bar[F]
}

That’s where it falls apart, unfortunately.

The value parameter isn’t used in the type! It appears so you can do extension [A] (a: A) { def k = () => a; type K = () => A } all in one go

It’s used here:

I.e Extended is a member of the type of f. So what does f.Extended expand to?

Oooh, because type members are accessed from the class? Hmm I’m confused now

It appears that the original example should desugar to something like this:

trait Foo
trait Bar[T]

erased def extension_F[F <: Foo](f: F): { type Extended = Bar[F] } = erasedValue

trait SpecificFoo[T] extends Foo

val f = new SpecificFoo[5]{}
val extended$f = extension_F(f)
summon[extended$f.Extended =:= Bar[SpecificFoo[5]]]

Since the original example includes a value parameter, it should generate a method to return a type with the type member that initiated an extension type member search

4 Likes

If only we could somehow generalize extension blocks to make them behave more uniformly with the rest of the language… Something more like a class… An extension class? Or shall we say an implicit class since it’s constructed implicitly? :thinking:

5 Likes

Hmm, I wonder where I’ve seen that before…

Yeah, you can do a similar thing with implicit classes, but you can’t get rid of the need for an explicit val assignment

trait Foo
trait Bar[T]

implicit class extension_F[F <: Foo](f: F) { 
  object Extended { type T = Bar[F] }
}

trait SpecificFoo[T] extends Foo

val f = new SpecificFoo[5]{}
val ext = f.Extended
@main def main = implicitly[ext.T =:= Bar[SpecificFoo[5]]]

In Scala 2 f.Extended.T doesn’t work because implicit search doesn’t start inside types, in Scala 3 it does start, but it’s not a stable path so you can’t select from it main$package.extension_F[F]#Extended.type is not a valid type prefix, since it is not an immutable path

[3]
[2]

3 Likes