At the end of the day, here is what I want to achieve :
val onePath: One = new Log(OneLocation("root"), "foo/bar").getPath()
val manyPath: Many = new Log(ManyLocation(List("base1", "base2")), "foo/bar").getPath()
In order to achieve that, it seems that an ADT representing one or many value(s) is required.
Here is my implementation. Is there another/better/simpler way to implement it (I've used path dependent type and F-bounded types). is there a library that already implements it (the use case seems prety current).
sealed trait OneOrMany[T <: OneOrMany[T]] {
def map(f: String => String) : T
}
final case class One(a: String) extends OneOrMany[One] {
override def map(f: String => String): One = One(f(a))
}
final case class Many(a: List[String]) extends OneOrMany[Many] {
override def map(f: String => String): Many = Many(a.map(f))
}
sealed trait Location {
type T <: OneOrMany[T]
def value: T
}
final case class OneLocation(bucket: String) extends Location {
override type T = One
override val value = One(bucket)
}
final case class ManyLocation(buckets: List[String]) extends Location {
override type T = Many
override val value = Many(buckets)
}
class Log[L <: Location](location: L, path: String) {
def getPath(): L#T = location.value.map(b => s"fs://$b/$path")
}
I am not sure if you actually need all that, why not just something like this?
Which works like this:
If you really, really, really want to have all those classes you can just do this:
Which works like you want: