【问题标题】:Scala polymorphic callback type mismatchScala多态回调类型不匹配
【发布时间】:2020-10-20 23:10:36
【问题描述】:

很抱歉,我找不到更好的标题。

我正在尝试实现以下目标

abstract class Person 

case class User(uid: String, firstname: String, active: String) extends Person
case class Admin(id: String, pseudo: String, securityClearance: String) extends Person

def innerFunctionForUser(user: User): List[String] = {
  List() :+ user.uid :+ user.firstname :+ user.active
}

def innerFunctionForAdmin(admin: Admin): List[String] = {
  List() :+ admin.id :+ admin.psuedo :+ admin.securityClearance
}

def outerFunction(person: Person, innerFunction: (Person) => List[String]): List[String] = {
  innerFunction(person)
}

所以我可以这样使用它

val myUser = User("0c60c5b4-306d-4372-b60d-fd699c80e408", "joe", "false")
val myAdmin = Admin("178789", "jack", "high")

outerFunction(myUser, innerFunctionForUser)
outerFunction(myAdmin, innerFunctionForAdmin)

不输入检查

type mismatch;
 found   : User => List[String]
 required: Person => List[String]

我不能让 innerFunction 接受这样的类型的人

def innerFunctionForUser(user: Person): List[String] = {
  List() :+ user.uid :+ user.firstname :+ user.active
}

我在这里保持简单,但我需要具有不同类型和不同参数数量的参数的案例类。 所以我不能在抽象类 Person 中声明它们。 哪个会给

value uid is not a member of Person

value firstname is not a member of Person

value active is not a member of Playground.Person

如何使具有不同类型和数字参数的不同案例类评估为相同类型?

与/或

如何使回调多态,就像这样

def outerFunction(person: Person, innerFunction: (SomeCaseClass) => List[String]): List[String] = {
  innerFunction(person)
}

希望这已经足够清楚了。

感谢阅读,祝你好运。

【问题讨论】:

  • 顺便说一句,List() :+ user.uid :+ user.firstname :+ user.active 不常见且不可读。你可以:List(user.uid, user.firstname, user.active)user.uid :: user.firstname :: user.active :: Nil (我会选择前者)。顺便说一句,看来你可能想看看typeclasses
  • 我意识到发布的代码可能是对您的实际需求的粗略简化,但如果您真正需要的是case class 参数中的List[String],那么def innerFunc(p:Product):List[String] = p.productIterator.map(_.toString).toList 将为您完成。
  • 是的,这只是为了举例说明,但有点愚蠢,谢谢。

标签: scala types callback polymorphism case-class


【解决方案1】:

UserAdminPerson 的子类型,但User => List[String]Admin => List[String] 不是Person => List[String]子类型User => List[String]Admin => List[String] 实际上是Person => List[String]超类型。函数类型A => B 相对于Bcovariant,但相对于A 是逆变的。

试着让outerFunctiongeneric

def outerFunction[P <: Person](person: P, innerFunction: P => List[String]): List[String] = 
  innerFunction(person)

outerFunction(myUser, innerFunctionForUser) //List(0c60c5b4-306d-4372-b60d-fd699c80e408, joe, false)
outerFunction(myAdmin, innerFunctionForAdmin) //List(178789, jack, high)

您也可以尝试将函数innerFunctionForUserinnerFunctionForAdmin替换为type class

trait InnerFunction[P <: Person] {
  def apply(person: P): List[String]
}

object InnerFunction {
  implicit val forUser: InnerFunction[User] = 
    user => List(user.uid, user.firstname, user.active)
  implicit val forAdmin: InnerFunction[Admin] = 
    admin => List(admin.id, admin.pseudo, admin.securityClearance)
}

def outerFunction[P <: Person](person: P)(implicit innerFunction: InnerFunction[P]): List[String] = 
  innerFunction(person)

outerFunction(myUser) //List(0c60c5b4-306d-4372-b60d-fd699c80e408, joe, false)
outerFunction(myAdmin) //List(178789, jack, high)

由于类型类InnerFunction 现在类似地作用于不同的数据类型(它为案例类的所有字段生成值列表),您甚至可以导出它:

trait InnerFunction[T] {
  def apply(t: T): List[String]
}

object InnerFunction {
  implicit def mkInnerFunction[T <: Product]: InnerFunction[T] =
    _.productIterator.map(_.asInstanceOf[String]).toList
}

def outerFunction[T](t: T)(implicit innerFunction: InnerFunction[T]): List[String] = 
  innerFunction(t)

      //or simply
// def outerFunction[T <: Product](t: T): List[String] =
//   t.productIterator.map(_.asInstanceOf[String]).toList
      //or
// def outerFunction(t: Product): List[String] =
//   t.productIterator.map(_.asInstanceOf[String]).toList

outerFunction(myUser) //List(0c60c5b4-306d-4372-b60d-fd699c80e408, joe, false)
outerFunction(myAdmin) //List(178789, jack, high)

(如果不是T 的所有字段都是Strings,这将在运行时失败)or

import shapeless.ops.hlist.ToList
import shapeless.{Generic, HList}

trait InnerFunction[T] {
  def apply(t: T): List[String]
}

object InnerFunction {
  implicit def mkInnerFunction[T <: Product, L <: HList](implicit
    generic: Generic.Aux[T, L],
    toList: ToList[L, String]
  ): InnerFunction[T] = generic.to(_).toList
}

def outerFunction[T](t: T)(implicit innerFunction: InnerFunction[T]): List[String] = 
  innerFunction(t)

      //or simply
// def outerFunction[T, L <: HList](t: T)(implicit
//   generic: Generic.Aux[T, L],
//   toList: ToList[L, String]
// ): List[String] = generic.to(t).toList

outerFunction(myUser) //List(0c60c5b4-306d-4372-b60d-fd699c80e408, joe, false)
outerFunction(myAdmin) //List(178789, jack, high)

(这将保证在编译时T 的所有字段都是Strings)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2013-03-17
    • 2019-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多