There are businesses and people. Users can either like or post a comment about a business but the same can not happen with a person. When a user posts something about a business or likes it, that business is called the target
of that like or post:
trait TargetingRelation[TargetingType[_],TargetedType]
class Business
class Person
class Post[Target | TargetingRelation[Business,Post] ] {
def target:Target
}
class Like[Target | TargetingRelation[Business,Like] ] {
def target:Target
}
Here I'm inventing a T | P[T]
notation meaning type parameter T
such that it satisfies some property P[T]
(or T :|: P[T]
if it carries more type-appeal). Somewhere else in the code I want to have declarations like:
object canPostAboutBusiness extends TargetingRelation[Post,Business]
object canLikeBusiness extends TargetingRelation[Like,Business]
these objects are evidences actually, something like Haskell type-classes. So this would type check:
val p = new Post[Business]
val l = new Like[Business]
but not this one:
val p = new Post[Person]
val l = new Like[Person]
As far as my knowledge of Scala permits, I can't model this particular state of affairs in a satisfactory way. Now I insist that this is not sub-typing because Business is not a:
class Business extends
TargetingRelation[Post,Business] with
TargetingRelation[Like,Business]
As a matter of fact it is very desirable that Business
remains totally ignorant of the Post
. The relationship is actually outside of both Post
and Business
. Besides, I suppose the above code would not even compile to begin with since Business
is inheriting from TargetingRelation
twice. Insights are most welcome.
You can do this in scala with something that acts like a typeclass using implicits. For example:
Then you can instantiate the classes with
Business
But not with
Person
If you search from "scala typeclasses", you'll find plenty of explanations of the details of how this works, but basically, you require the constructor to take an implicit parameter of the type
TargetingRelation[TargetingType[_],TargetedType]
and then place an implicit of that type in scope when constructing your class (Post
orLike
). The implicit serves as "evidence" that theTargetedType
has an instance of the typeclass (and plays the role of the explicit dictionary of methods that get automatically passed around in other languages typeclass implementations).In fact, scala has some synatic sugar to help with this, called the Context Bound. This causes methods written as:
to be translated to
In your particular example, the contexts bounds syntax is a little tricky because there are multiple type parameters, but it can be done as this SO question describes.