Ash*_*ary 4 generics scala typeclass parametric-polymorphism type-constructor
有企业和人.用户可以喜欢或发布关于商家的评论,但对于一个人来说也不会发生同样的事情.当用户发布有关某个商家或喜欢它的内容时,该商家称之为target喜欢或发布:
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
}
Run Code Online (Sandbox Code Playgroud)
在这里,我发明了一种T | P[T]符号意义类型参数T,使其满足某些属性P[T](或者T :|: P[T]如果它具有更多的类型吸引力).代码中的其他地方我想要声明如下:
object canPostAboutBusiness extends TargetingRelation[Post,Business]
object canLikeBusiness extends TargetingRelation[Like,Business]
Run Code Online (Sandbox Code Playgroud)
这些对象实际上是证据,类似于Haskell类型类.所以这会打字检查:
val p = new Post[Business]
val l = new Like[Business]
Run Code Online (Sandbox Code Playgroud)
但不是这个:
val p = new Post[Person]
val l = new Like[Person]
Run Code Online (Sandbox Code Playgroud)
就我对Scala的认识而言,我无法以令人满意的方式对这种特殊情况进行建模.现在我坚持认为这不是子类型,因为商业不是:
class Business extends
TargetingRelation[Post,Business] with
TargetingRelation[Like,Business]
Run Code Online (Sandbox Code Playgroud)
事实上,非常希望Business仍然完全不知道Post.的关系实际上是两外Post和Business.此外,我认为上面的代码甚至不会编译开始,因为Business它继承了TargetingRelation两次.很受欢迎.
related:使用类类型参数中的上下文绑定
您可以在scala中执行此操作,其中某些操作类似于使用implicits的类型类.例如:
import scala.language.higherKinds
trait TargetingRelation[A[_], B]
class Business
class Person
// Using explicitly declared implicit parameter:
class Post[T](implicit ev: TargetingRelation[Post, T])
// Using a "context bound". The syntax is a little hairy and uses
// a type lambda because TargetingRelation takes multiple type params
class Like[T : ({type S[x] = TargetingRelation[Like, x]})#S]
implicit object canPostAboutBusiness extends TargetingRelation[Post,Business]
implicit object canLikeBusiness extends TargetingRelation[Like,Business]
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用实例化类 Business
scala> val p = new Post[Business]
p: Post[Business] = Post@374c991a
scala> val l = new Like[Business]
l: Like[Business] = Like@1fd348f8
Run Code Online (Sandbox Code Playgroud)
但不是 Person
scala> val p1 = new Post[Person]
<console>:15: error: could not find implicit value for parameter ev: TargetingRelation[Post,Person]
val p1 = new Post[Person]
^
scala> val p2 = new Like[Person]
<console>:15: error: could not find implicit value for evidence parameter of type TargetingRelation[Post,Person]
val p2 = new Like[Person]
^
Run Code Online (Sandbox Code Playgroud)
如果你从"scala类型类"中搜索,你会发现有关它如何工作的细节的大量解释,但基本上,你需要构造函数采用该类型的隐式参数,TargetingRelation[TargetingType[_],TargetedType]然后在范围内放置一个隐含的类型.构建你的类(Post或Like).隐式充当"证据",即TargetedType具有类型类的实例(并且扮演在其他语言类型类实现中自动传递的显式方法字典的角色).
实际上,scala有一些同步糖来帮助解决这个问题,称为Context Bound.这导致方法写为:
def a[A: B] = ???
Run Code Online (Sandbox Code Playgroud)
被翻译成
def a[A](implicit ev: B[A]) = ???
Run Code Online (Sandbox Code Playgroud)
在您的特定示例中,上下文边界语法有点棘手,因为有多个类型参数,但可以按照此SO问题描述来完成.