如何在运行时使用类型类或隐式类将ad-hoc多态性引入参数化上下文?



我有一组adt,如下面提到的Person和一组隐式类实现(在我的情况下)。这一切都很好,直到我试图在一个方法中抽象一个共同的代码块,比如在本例中是display。然而,不确定如何实现这一目标。我确实理解以下错误,但我想知道是否有办法实现这一目标,如果可能的话。请建议。TIA。

隐式类实现

case class Person(firstName: String, lastName: String)
trait CustomStringifier {
def stringify(): String
}
implicit class PersonCustomStringifier(person: Person) extends CustomStringifier {
def stringify(): String = s"My name is ${person.firstName} ${person.lastName}."
}
def display[T](obj: T): Unit = {
println(obj.stringify())
}
display(Person("John", "Doe"))

这一次我正确地得到编译时错误在行println(obj.stringify):Cannot resolve symbol stringify

类型类实现

case class Person(firstName: String, lastName: String)
trait CustomStringifier[T] {
def stringify(x: T): String
}
implicit object PersonCustomStringifier extends CustomStringifier[Person] {
def stringify(person: Person): String =
s"My name is ${person.firstName} ${person.lastName}."
}
implicit class Stringifier[T](x: T) {
def stringify(implicit stringifier: CustomStringifier[T]): String =
stringifier.stringify(x)
}
def display[T](obj: T): Unit = {
println(obj.stringify)
}
println(Person("John", "Doe").stringify)

我正确地在println(obj.stringify):NO implicits found for parameter stringifier: CustomStringifier[T]行得到编译时错误。

注:如果我需要重新定义这个问题,让它更有意义,请告诉我。虽然,我尝试添加多个工作代码片段来指示我真正需要实现的目标。

类型类是在编译时解析的,所以当你看到这个:

Person("John", "Doe").stringify

编译器生成如下:

new Stringifier(Person("John", "Doe")).stringify(PersonCustomStringifier)

因为它知道T=Person,它需要CustomStringifier[T]从隐式作用域,并且PersonCustomStringifier是这样隐式的。

stringify方法你有:

def stringify(implicit stringifier: CustomStringifier[T]): String =
stringifier.stringify(x)

编译器不知道T类型,但它知道它有一些CustomStringifier[T]可以在隐式作用域中找到-因为您将其作为隐式参数传递。然后,学习T和解析相关CustomStringfier[T]的责任被委托/延迟给调用者(如果它还使用类型参数,调用者可能会进一步委托)。

当你写:

def display[T](obj: T): Unit = {
println(obj.stringify)
}

T是未知的。范围内没有CustomSerializer[T],只有PersonCustomStringifier-但是我们没有证据证明T=Person,所以我们不能使用它。这就是编译失败的原因。

要解决这个问题,你必须采用隐式参数,将解析推迟给调用者

def display[T](obj: T)(implicit stringify: CustomStringifier[T]): Unit =
println(obj.stringify)

可以缩写为

def display[T: CustomStringifier](obj: T): Unit =
println(obj.stringify)

那么当你打电话的时候

display(Person("John", "Doe"))

编译器将知道T=Person,并且能够计算出PersonCustomStringifier是作为隐式实参传递的正确隐式。

首先,简而言之,typeclass是一个泛型函数,它通过某种机制(在我们的例子中是隐式搜索机制)从有限数量的具体函数中解析其实现。在scala中,它由4个组件组成:

  1. 函数接口声明(trait)
  2. 对象,它提供了类似func(smith)的语法,是trait
  3. 的伴侣。
  4. 有限实现集-隐式值
  5. (可选)扩展,提供smth。函数的语法。

隐式的一般用法如下:

trait YourTypeclass[T] {
def doStuff(t: T): R
} 
object YourTypeclass {
def apply[T](t: T)(implicit tInstance: YourTypeclass[T]): R = 
tInstance.doStuff(t)
}
implicit YourTypeclassSyntax[T](val t: T) {
def doStuff[T](implicit tInstance: YourTypeclass[T]): R = tInstance.doStuff(t)
}

实现:


case class YourClass()
object YourClass {
implicit val yourTypeclassInstance: YourTypeclass[T] = {t => ???: R}
}

用法:

val x: YourClass = ???
YourTypeclass(x)
x.doStuff
implicitly[YourTypeclass[T]].doStuff(t)

那么为什么把实例放在伴侣对象上很重要呢?因为隐含的搜索顺序。在同伴对象中定义的实例,如果它们不受private[package](隐式优先级)的限制,则可以从项目的每个地方使用。

你也可以通过把隐式实例放在trait中来控制你想要使用的隐式实例,然后更深的隐式是,然后优先级更低(对于泛型和覆盖很有用,比如List[T]vsList[Int])。

第二,对于您的特殊场合,Show[T]from cats存在。

也有简短的解释,有例子:https://typelevel.org/cats/typeclasses.html

相关内容

  • 没有找到相关文章

最新更新