scala - 对两种类型之间的二元关系建模

标签 scala generics 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
}

我在这里发明了一个 T | P[T]符号含义类型参数T使得它满足某些性质 P[T] (或 T :|: P[T] 如果它具有更多类型吸引力)。在代码中的其他地方,我想要声明如下:
object canPostAboutBusiness extends TargetingRelation[Post,Business] 
object canLikeBusiness      extends 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:
class Business extends 
  TargetingRelation[Post,Business] with 
  TargetingRelation[Like,Business]

事实上,Business 是非常可取的。仍然完全不知道 Post .这种关系实际上是在两者之外 PostBusiness .此外,我想上面的代码甚至无法编译,因为 Business继承自 TargetingRelation两次。见解是最受欢迎的。

相关:Using a context bound in a class type parameter

最佳答案

您可以在 Scala 中使用隐式函数,使用类似于类型类的东西来执行此操作。例如:

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]

然后你可以用 Business 实例化这些类
scala> 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: 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]
                ^

如果你从“scala typeclasses”中搜索,你会发现很多 explanations of the details这是如何工作的,但基本上,您需要构造函数采用 TargetingRelation[TargetingType[_],TargetedType] 类型的隐式参数。然后在构造类( PostLike )时在范围内放置该类型的隐式。隐式作为 TargetedType 的“证据”有一个类型类的实例(并扮演在其他语言类型类实现中自动传递的方法的显式字典的角色)。

事实上,scala 有一些合成糖可以帮助解决这个问题,称为 Context Bound .这会导致方法写为:
def a[A: B] = ???

翻译成
def a[A](implicit ev: B[A]) = ???

在您的特定示例中,上下文边界语法有点棘手,因为有多个类型参数,但可以作为 this SO question 完成。描述。

关于scala - 对两种类型之间的二元关系建模,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20438322/

相关文章:

scala - 为什么更喜欢 Typeclass 而不是 Inheritance?

haskell - Haskell 类型类中的乘积和求和类型并行

javascript - 创建 Scala.js 库 - 将 Scala 链接到 Javascript

java - 接口(interface)的泛化导致编译错误

generics - 过滤通用类型,无需反射或转换

c# - 非常简单的代码编译但在执行时抛出 TypeLoadException

scala - Java 输入/输出流的函数式编程方法

ruby-on-rails - 关于 Rails 前端和 Scala 后端之间通信的建议

scala - 如何使用隐式参数实现FRP?

scala - 在 Scala 中实现产品类型,并在其各个部分上使用通用更新功能