According to the book Advanced Programming in Scala 5th edition wrriten by Martin Ordersy
Note that adding an upper bound to an opaque type doesn’t act like the extends keyword does when defining a class. In particular, an opaque type does not inherit the interface of its upper bound. For example, although you can pass a NonEmptyString where a String is required, you cannot invoke methods declared on class String on a NonEmptyString:
object NonEmptyStrings:
opaque type NonemptyString <: String = String
object NonEmptyString:
def apply(S: String): NonEmptyString =
require(s.nonEmpty)
s
def from(s: String): Option[NonEmptyString] =
if s.nonEmpty then Some(s) else None
……………………………………
// in a different .scala file
import NonEmptyStrings._
"hi".charAt(1) // i
NonEmptyString("hi").charAt(1)
1 |NonEmptyString("hi").charAt(1)
|ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
|value charAt is not a member of
|NonEmptyStrings.NonEmptyString
The only methods you will be able to invoke on an opaque type are those defined on Any and those added via any extension methods defined for the opaque type, even if the opaque type is given an upper bound
But when I coded like that, no exception occured.The NonEmptyString("hi").charAt(1)
runned normally and the result can be printed to i
But when I code like that, no exception occured.The NonEmptyString("hi").charAt(1)
run normally and the result can println into console showing i
.
My practice is not like the book predicted.
After a short discussion with one of the book's authors, I confirm this is incorrect or outdated, and should be fixed in the next edition.