https://scastie.scala-lang.org/60nHfRoiRVONgdxGJpUjZA
opaque type Foo = Int
object Foo
def apply(i: Int): Foo = i
opaque type Bar = Int
object Bar
def apply(i: Int): Bar = i
// ******************************
// Bug example #1, Bar incorrectly acts as subtype of Foo
// when used in a top-level definition.
object FooToString
// Next line gives a compilation error as expected: "Found: Bar => String, Required: Foo => String"
//val fooToString: Foo => String = (b: Bar) => "hi from Bar function"
// This line is identical to the previous line, but it's a
// top-level definition that compiles and is used below.
val fooToString: Foo => String = (b: Bar) => "hi from Bar function" // BUG
// ******************************
// Bug example #2, same as #1 but demonstrated using
// a typeclass instance top-level definition.
trait Show[A]
def show(a: A): String
object Show
// Next line gives a compilation error as expected: "Found: Bar => String, Required: Show[Foo]"
//given Show[Foo] = (f: Bar) => "hi from Show[Foo]"
// This line is identical to the previous line, but it compiles,
// creating an instance of Show[Foo] that's used below.
given Show[Foo] = (f: Bar) => "hi from Show[Foo]" // BUG
def show[A: Show](a: A): String = summon[Show[A]].show(a)
object Main extends App
println(show(Foo(5)))
println(fooToString(Foo(17)))
As shown in the Scastie snippet, this definition
val fooToString: Foo => String = (b: Bar) => "hi from Bar function"
Is expected to fail compilation with the error Found: Bar => String, Required: Foo => String but instead compilation succeeds if the definition is top-level.
In fact, opaque types are transparent in the scope they appear, not just in their companion object.
For toplevel opaques this means that the definition is visible in all other toplevel definitions of the same file, since these go into one package object together. Objects or classes in the same file do not get wrapped in the object and are consequently our of the scope where an opaque alias is visible.
I have added a section to the docs that explains this.
opaque types are transparent in the scope they appear, not just in their companion object
Got it.
Might we consider a mechanism to make this property known to users at compile-time? Something like
Warning W037:
at 12: given Show[Foo] = (f: Bar) => "hi from Show[Foo]"
^
The value of type `Bar => String`
(f: Bar) => "hi from Show[Foo]"
is assignable to
given Show[Foo]
because "opaque type Bar = Long <: opaque type Foo = Long"
and opaque types are transparent in the scope
they are defined, not just their companion objects.
Learn more at www.allaboutopaquetypes.com/W037.
Disable this warning with --stop-whining-about-opaque-types
TIL I don't understand what "scope" means. I thought it meant literally what is "seen".
So I would have said that this disposition is at odds with https://github.com/lampepfl/dotty/issues/7891
But scope does not mean the same thing as telescope or kaleidoscope. It means a target to aim at (skopos). The figurative sense is that the scope of one's power includes all the targets one can hit.
Shakespeare: "Desiring this mans art, and that mans skope." (Sonnet 29, in which he is depressed about his prospects, then gets a mood adjustment from his lover.)
I would still prefer that scope is what I can tell by looking.
Except, of course, for implicit scope.
"Opaque" means obscure because it's in shadow. It's too bad we can't use opaque for shadowed symbols. The sense of "not translucent" comes afterward, from optical science.
OED has another nice line from Spenser: "slipper hope Of mortal men, that swincke and sweate for nought, And shooting wide, doe misse the marked scope." There is an old word "swinch" for "toil", which someone may want to use for their latest Scala project.
@ryanberckmans In my experience it is really hard to do something like what you propose without producing false negatives or annoying warnings.
Most helpful comment
TIL I don't understand what "scope" means. I thought it meant literally what is "seen".
So I would have said that this disposition is at odds with https://github.com/lampepfl/dotty/issues/7891
But scope does not mean the same thing as telescope or kaleidoscope. It means a target to aim at (skopos). The figurative sense is that the scope of one's power includes all the targets one can hit.
Shakespeare: "Desiring this mans art, and that mans skope." (Sonnet 29, in which he is depressed about his prospects, then gets a mood adjustment from his lover.)
I would still prefer that scope is what I can tell by looking.
Except, of course, for implicit scope.
"Opaque" means obscure because it's in shadow. It's too bad we can't use opaque for shadowed symbols. The sense of "not translucent" comes afterward, from optical science.
OED has another nice line from Spenser: "slipper hope Of mortal men, that swincke and sweate for nought, And shooting wide, doe misse the marked scope." There is an old word "swinch" for "toil", which someone may want to use for their latest Scala project.