2

I am reading in dread what will come with Scala 3, paying particular attention to changes to compound types. They were always somewhat of a hack, so clean, true intersection types are certainly an improvement. I couldn't find though anything about what happens to the actual refinement part of the compound type. I rely heavily in my current project on strongly interwoven types in an attempt to have every returned value be as narrow as possible. So, for example, having

trait Thing { thisThing =>
    type A
    type B
    type C

    def something :Thing { 
        type A = <related to thisThing.A> 
        type B <: <related to thisThing.B>
        type C = <etc>
    }

Is it even still possible? How to achieve this goal with the new semantics? As I understand, refinements of abstract types will almost certainly be not allowed, so it will be difficult to have a 'self type' declaration in a root of a type hierarchy:

trait Thing {
    type ThisThing <: Thing
    type A
    type B
    def copy(...) :ThisThing { type A = ...; type B = ... }
}

On a somewhat related note, I was looking at structural types. I must say I like how we can dynamic member selection/implementation with static declarations. Is it though possible for abstract classes? Could I write something like:

trait Record(members :(String, Any)*) extends Selectable { ... }

val r = new Record { val name :String; val age :Int }

EDIT: I found the relative bit of documentation and it looks like anonymous Selectable instance are exempt from the rule that the type of an anonymous class is the intersection of its extended types - good.

Turin
  • 2,208
  • 15
  • 23
  • 3
    Where did you get that this would not be possible? – Jasper-M Sep 26 '20 at 14:11
  • IIRC you'll only need `Selectable` if you want a refinement type inferred, otherwise, it'll work pretty much the same as in Scala 2 – user Sep 26 '20 at 22:54

1 Answers1

4

As I understand, refinements of abstract types will almost certainly be not allowed...

type A <: {
  type U
}

trait B {
  type T <: A
}

object o extends B {
  type T = A { type U = Int }
}

perfectly compiles.

https://scastie.scala-lang.org/Nbz3GxoaTSe3VhXZz406vQ

What is not allowed is type projections of abstract types T#U

trait B {
  type T <: A
  type V = T#U // B.this.T is not a legal path since it is not a concrete type
}

https://scastie.scala-lang.org/xPylqOOkTPK9CvWyDdLvsA

http://dotty.epfl.ch/docs/reference/dropped-features/type-projection.html

Maybe you meant that

type A {
  type U
}

is not parseable. But in Scala 2 it isn't either. Correct is either

trait A {
  type U
}

or

type A <: {
  type U
}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Weird. I distinctly remember commenting on this yesterday, but evidently didn't post it for some reason. Thanks for clearing that up - it is possible I had that mixed up with volatility. I was aware that type projections are dropped (the main reason for that feelng of dread) and I must have extended it in my mind to refinements. Are volatile types still a thing, or did the DOT calculus tighten things up enough for them to not be needed? – Turin Sep 27 '20 at 08:55
  • @Turin I guess volatile types is a Scala-2 thing. I guess they were a workaround to avoid issues caused by unsoundness of type projections https://stackoverflow.com/questions/15880438/cannot-override-a-type-with-non-volatile-upper-bound – Dmytro Mitin Sep 27 '20 at 12:04