建模两种类型之间的二元关系 [英] Modeling a binary relationship between two types

查看:152
本文介绍了建模两种类型之间的二元关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有企业和人员。用户可以喜欢或发表关于企业的评论,但是不能与某人发生相同。当用户发布关于企业或喜欢的东西时,该企业被称为目标的类似或发布:



< pre $ trait TargetingRelation [TargetingType [_],TargetedType]

class商业

类Person

class发布[目标| TargetingRelation [Business,Post]] {
def target:Target
}

class Like [Target | TargetingRelation [Business,Like]] {
def target:Target
}

这里我发明了一个 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@1fd348f8

But 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.

这篇关于建模两种类型之间的二元关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆