-
-
Save axelGschaider/8408802 to your computer and use it in GitHub Desktop.
trait Model[A <: Model[A]] { | |
val internalKey:String | |
def key:Key[A] = Key[A](internalKey) | |
// removed this function because it did not make my intent clear. | |
// def isThatYou(k:Key[A]) = key == k | |
} | |
case class Key[A <: Model[A]]( keyVal:String ) | |
case class ClassOne(internalKey:String) extends Model[ClassOne] // <- need to retype the type . . . error prone | |
case class ClassTwo(internalKey:String) extends Model[ClassTwo] | |
case class ClassThree(internalKey:String) extends Model[ClassTwo] // See! Here I went wrong with retyping. Not all that | |
// bad though. Trying to define a DAO[ClassThree] will | |
// result in compile time errors. | |
// this is the use case I really need! | |
// Trying to use a Key[ClassOne] on a DAO[ClassTwo] will result in a compiletime error | |
trait DAO[A <: Model[A]] { | |
def get(key:Key[A]):Option[A] = None // dummy implementation | |
} | |
// this test | |
object Test { | |
// old demonstration code. Commented it since it didn't communicate what I was trying to do. | |
// def works = ClassOne("foo").isThatYou( ClassOne("bar").key ) | |
// def fails = ClassOne("foo").isThatYou( ClassTwo("bar").key ) // <- compiler error. As expected. | |
// // ClassTwo key can't be used with ClassOne | |
val oneDAO = new DAO[ClassOne](){} | |
// usually we would get our keys somehow differently | |
// this is just for demonstration reasons | |
val oneKey = ClassOne("123").key | |
val twoKey = ClassTwo("456").key | |
val someOne:Option[ClassOne] = oneDAO.get( oneKey ) //works | |
oneDAO.get( twoKey ) //fails to compile . . . as expected | |
} |
Here you don't parameterize classes and you don't get desired compiler error:
trait Model {
val internalKey: String
def key: Model
def isThatYou(k: Model) = key == k
}
case class ClassOne(internalKey:String) extends Model {
def key: Model = ClassOne(internalKey)
}
case class ClassTwo(internalKey:String) extends Model {
def key: Model = ClassTwo(internalKey)
}
val k1 = ClassOne("a")
val k2 = ClassTwo("a")
k1.isThatYou(k2) // false
k1.isThatYou(k1) // true
val l: List[Model] = List(k1, k2) // l: List[Model] = List(ClassOne(a), ClassTwo(a))
One more option - you get desired compiler error and don't have to parameterize classes but you have to define type A within subclasses:
trait Model {
type A <: Model
val internalKey: String
def isThatYou(k: A) = this == k
}
case class ClassOne(internalKey:String) extends Model {
type A = ClassOne
}
case class ClassTwo(internalKey:String) extends Model {
type A = ClassTwo
}
val k1 = ClassOne("a")
val k2 = ClassTwo("a")
k1.isThatYou(k2) // error: type mismatch; found : ClassTwo required: k1.A
k1.isThatYou(k1) // true
val l: List[Model] = List(k1, k2) // l: List[Model] = List(ClassOne(a), ClassTwo(a))
Now I think I understand what it was about:
trait Model {
self =>
val internalKey: String
def isThatYou(k: self.type) = this == k
}
case class ClassOne(internalKey:String) extends Model
case class ClassTwo(internalKey:String) extends Model
val k1 = ClassOne("a")
val k2 = ClassTwo("a")
k1.isThatYou(k2) // error: type mismatch; found : ClassTwo required: k1.A
k1.isThatYou(k1) // true
val l: List[Model] = List(k1, k2) // l: List[Model] = List(ClassOne(a), ClassTwo(a))
Hi izmailoff
many many thanks for your work and time. Really appreciate it!
But actually the point of the whole thing was to have that parameterized Key trait. I'm working in a domain where I will really need keys. But instead of having keys like pure Strings that I can (falsely) stuff everywhere, I want keys that only "fit certain locks".
so for example if there where Book and Car classes. The BookDAO would only accept keys of the type Key[Book] and of course not Key[Car]. So I can't accidentally misuse a key. In the haskell world I saw something like this in the yesod framework, fell in love with it and am now trying to mimic it in scala . . .
I'll be adding a DAO trait to the gist to make my intent more clear.
Not sure if that's what you are looking for, you still have to parameterize classes in this case.
trait Model[A <: Model[A]] {
val internalKey:String
def isThatYou(k: A) = this == k
}
case class ClassOne(internalKey:String) extends Model[ClassOne]
case class ClassTwo(internalKey:String) extends Model[ClassTwo]
val k1 = ClassOne("a")
val k2 = ClassTwo("a")
k1.isThatYou(k2) // error: type mismatch; found : ClassTwo required: ClassOne
k1.isThatYou(k1) // true
scala> val l = List(k1, k2)
l: List[Product with Serializable with Model[_ >: ClassTwo with ClassOne <: Product with Serializable with Model[_ >: ClassTwo with ClassOne <: Product with Serializable]]] = List(ClassOne(a), ClassTwo(a))