这里我发明了一个 T | P [T] 符号含义类型参数 T 使得它满足某些属性 P [T] (或 T:|:P [T] ,如果它携带更多类型上诉)。
object canPostAboutBusiness extends TargetingRelation [Post,Business] object在代码中的其他地方,我想要声明如下: canLikeBusiness扩展了TargetingRelation [Like,Business]这些对象实际上是证据,类似于Haskell类型类。所以这会检查:
val p = new Post [Business] val l = new Like [Business]但不是这一个:
val p = new Post [Person] val l = new Like [Person]就我对Scala的认识而言,我无法以令人满意的方式模拟这种特殊情况。现在我坚持认为这是不是子分型,因为业务不是 a:
类业务扩展 TargetingRelation [发布,业务]与 TargetingRelation [喜欢,商业]事实上, Business 完全不知道 Post 。这种关系实际上在发布和商业之外。此外,我想上面的代码甚至不会编译,因为 Business 从 TargetingRelation >继承两次。洞察力是最受欢迎的。
相关:使用上下文绑定的类类型参数
解决方案你可以在scala中用类似typeclass的东西来使用implicits。例如:
import scala.language.higherKinds trait TargetingRelation [A [_],B ] class Business class Person //使用显式声明的隐式参数: class Post [T](imp ev:TargetingRelation [Post, T]) //使用上下文绑定。语法有点毛,并且使用 //一个类型lambda,因为TargetingRelation需要多个类型参数 class Like [T:({type S [x] = TargetingRelation [Like,x]})#S ] 隐式对象canPostAboutBusiness extends TargetingRelation [Post,Business] 隐式对象canLikeBusiness extends TargetingRelation [Like,Business]然后你可以通过 Business
阶> val p = new Post [Business] p:Post [Business] = Post @ 374c991a scala> val l = new Like [Business] l:Like [Business] = Like @ 1fd348f8但不能与 Person
scala> val p1 = new Post [Person] < console>:15:error:找不到参数ev的隐式值:TargetingRelation [Post,Person] val p1 = new Post [Person] ^ scala> val p2 = new Like [Person] < console>:15:error:找不到类型为TargetingRelation [Post,Person]的证据参数的隐式值 val p2 = new Like [Person]如果从scala typeclasses搜索,会发现很多详细解释,但基本上,您需要构造函数获取类型 TargetingRelation [TargetingType [_],TargetedType] 的隐式参数,然后放置在构造类时( Post 或 Like )),范围内隐含该类型。隐式作为证据用于证明 TargetedType 具有typeclass的一个实例(并且扮演显式字典中的方法的角色,这些方法在其他语言中自动传递类型类实现)。
事实上,scala有一些可以帮助解决这个问题的合成糖,叫做上下文绑定。这导致方法写成:
def a [A:B] = ???被翻译为
def a [A](隐式ev:B [A])= ???在您的特定示例中,上下文边界语法有点棘手,因为有多个类型参数,但它可以完成为这个SO问题描述。
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.
related : Using a context bound in a class type parameter
解决方案You can do this in scala with something that acts like a typeclass using implicits. For example:
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]Then you can instantiate the classes with Business
scala> val p = new Post[Business] p: Post[Business] = Post@374c991a scala> val l = new Like[Business] l: Like[Business] = Like@1fd348f8But not with 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] ^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 or Like). The implicit serves as "evidence" that the TargetedType 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:
def a[A: B] = ???to be translated to
def a[A](implicit ev: B[A]) = ???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.
更多推荐
建模两种类型之间的二元关系
发布评论