I think there is something I don't quite well understand when I have the following code:
trait Configuration[F[_]] {
def get(key: String): F[ConfigValue]
}
class InMemoryConfig extends Configuration[Option] {
override def get(key: String): Option[ConfigValue] = ???
}
class InFileConfig(path: String) extends Configuration[Try] {
override def get(key: String): Try[ConfigValue] = ???
}
trait Logging {
def config: Configuration[_] // does not work
// ...
}
class DefaultLogging {
override val config = new InMemoryConfig
// ...
}
The name of the classes are pretty much meaningless, the general goal is to have a member in the trait without defining the param type in order to delay the choice until the implementation (actually in the DefaultLogging
class).
Since Logging
doesn't know which type constructor will be used for the Configuration
it has to carry over it like any other type parameter.
Thus:
trait Logging[F[_]] {
def config: Configuration[F]
// ...
}
The same will apply to whatever depends on Logging
it needs to specify the type or keep the dependency.
Thus, a common observation would be if such direct dependency is needed or not, but that becomes a matter of design and sometimes personal preference.