-
-
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 | |
} |
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.
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))