Skip to content

Instantly share code, notes, and snippets.

@felipeblassioli
Created October 4, 2023 03:10
Show Gist options
  • Save felipeblassioli/2fbcda0ee8e788411dbefb6bdaad94b0 to your computer and use it in GitHub Desktop.
Save felipeblassioli/2fbcda0ee8e788411dbefb6bdaad94b0 to your computer and use it in GitHub Desktop.
Scala2 and Scala3 type projections test
trait Logger {
protected def formatMessage(message: String): String
def log(message: String): Unit = {
println(formatMessage(message))
}
// Nested case class inside the trait
case class LogEntry(timestamp: Long, content: String)
}
object Logger {
def apply(): Logger = new Logger {
override protected def formatMessage(message: String): String =
s"${System.currentTimeMillis()}: $message"
}
// Create a LogEntry instance using the nested case class in the trait
def createLogEntry(logger: Logger, content: String): logger.LogEntry =
logger.LogEntry(System.currentTimeMillis(), content)
// Process a LogEntry instance
def processLogEntry(entry: Logger#LogEntry): Unit =
println(s"Processing log entry: Timestamp = ${entry.timestamp}, Content = ${entry.content}")
}
// Usage
val logger = Logger()
val entry = Logger.createLogEntry(logger, "This is a log entry.")
Logger.processLogEntry(entry)
@felipeblassioli
Copy link
Author

GPT 4 explanation

The #` symbol is used in Scala to refer to a type that is a member of some class or trait without specifying a particular instance of that class or trait. This is often referred to as a path-dependent type.

Explanation of Logger#LogEntry:

  • Logger: Specifies the class or trait that contains the type.
  • #: Indicates that we're referring to a member type of Logger, not to something related to a particular instance of Logger.
  • LogEntry: The name of the nested type within Logger.

So, Logger#LogEntry means:

We're referring to the type LogEntry that is a member of any instance of Logger. We're not saying which specific Logger instance we're talking about - it could be any instance.

More Detail:

1. Path-Dependent Type:

In Scala, when you nest a class or trait within another class or trait, each instance of the enclosing class can have a different instance of the nested class. These nested class instances are tied to their specific enclosing class instance. This is called a path-dependent type because the type is dependent on the "path" or instance of the enclosing class.

2. Type Projection (#):

Scala uses the # symbol to refer to a nested type of any instance of an enclosing class or trait, without specifying which particular instance it’s nested in. This is called type projection, projecting the nested type out of its enclosing type.

Example for Clarity:

Consider this example:

trait Logger {
  case class LogEntry(content: String)
}

def printLog(entry: Logger#LogEntry): Unit = println(entry.content)
  • Path-Dependent Type: If we had a specific instance of Logger, say logger, and we wanted a method to accept only LogEntry instances created from that specific logger, we would use the path-dependent type syntax: logger.LogEntry.

  • Type Projection: On the other hand, if we want the method to accept LogEntry instances from any Logger, not just a specific one, we use the type projection syntax: Logger#LogEntry.

In essence, # allows you to refer to a nested type of a class or trait generically, without tying to a specific instance of the enclosing class or trait. It's particularly useful in situations where you want to work with a nested type but don't want to be constrained to a specific "path" or instance of the enclosing type.`

@felipeblassioli
Copy link
Author

felipeblassioli commented Oct 4, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment