在 Scala 中将类型 lambda 与更高级的类型一起使用:如何让编译器正确推断类型?

格言

假设我有代表多态函数之类的特征,例如:

trait Func[A[X, Y]] {
  def apply[X, Y](a: A[X, Y]): A[X, Y]
}

现在我想通过将类型 lambda 作为参数传递来将我的特征用作非多态函数:

type single[T] = { type t[X, Y] = T }
val aInstance: Func[single[String]#t] = 
  new Func[single[String]#t] {
    def apply[X, Y](a: String): String = ???
  }

现在我有方法test可以做一些有用的事情func,例如

def test[A[X, Y]](f: Func[A]): Unit = ???

我想调用testaInstance,而无需手动指定类型参数:

test(aInstance)

不幸的是,此代码不会编译(但test[single[String]#t](aInstance)会)并带有错误消息:

[error] /test.scala:16:3: no type parameters for method test: (f: Func[A])Unit exist so that it can be applied to arguments (Func[[X, Y]String])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : Func[[X, Y]String]
[error]  required: Func[?A]
[error]   test(aInstance)
[error]   ^
[error] /test.scala:16:8: type mismatch;
[error]  found   : Func[[X, Y]String]
[error]  required: Func[A]
[error]   test(aInstance)
[error]        ^
[error] two errors found

我的问题是:如何修改这些声明以允许编译器自动推断所有必需的类型?


对于那些想知道为什么我声明Func为拥有[X, Y]但从未在实际代码中使用它们的人,有一个更真实、更不抽象的例子:

object GenericTest {
  trait Item { def name: String }
  class ItemA extends Item { def name: String = "a" }
  class ItemB extends Item { def name: String = "b" }

  trait MapFn[-A[X <: Item], +B[X <: Item]] {
    def apply[X <: Item](data: A[X]): B[X]
  }

  case class ItemsCollection[C[A <: Item]](a: C[ItemA], b: C[ItemB]) {
    def map[D[A <: Item]](f: MapFn[C, D]): ItemsCollection[D] =
      ItemsCollection(f(a), f(b))
  }

  // sometimes we want to store sequences...
  val itemSeq = ItemsCollection[Seq](Seq(new ItemA), Seq(new ItemB))
  // or transform them:
  val itemSet = itemSeq.map(new MapFn[Seq, Set] {
    override def apply[X <: Item](data: Seq[X]): Set[X] = data.toSet
  })

  // but one day we wanted to store just objects without any item-specific types... e.g. names:
  type single[X] = { type t[A] = X }
  val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
    override def apply[X <: Item](data: Seq[X]): String = data.head.name
  })

/*
[error] test.scala:28:27: no type parameters for method map: (f: MapFn[Seq,D])ItemsCollection[D] exist so that it can be applied to arguments (MapFn[Seq,[A]String])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : MapFn[Seq,[A]String]
[error]  required: MapFn[Seq,?D]
[error]   val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
[error]                           ^
[error] test.scala:28:31: type mismatch;
[error]  found   : MapFn[Seq,[A]String]
[error]  required: MapFn[Seq,D]
[error]   val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
[error]                               ^
[error] two errors found
 */
}
斯蒂芬·康波

参考您的GenericTest,由于这个封闭但未修复的错误,无法让 Scala 推断出该形状

您可以做的一件事是尝试调整Unapply 的技术,使用隐式解析来确定 的可能候选者D这可能需要定义您自己的类型类和实例,而不是使用 Scalaz 提供的类型类和实例,并且可能需要更改MapFn声明方式以更适合此模式。确保为您single提供的实例具有最低优先级,因为它始终可以使用(每个T都是F[T]if F = Id)。

如果您控制 的定义MapFn,您还可以将B类型参数移动到类型成员。那么签名map就变成了

def map(f: MapFn[C]): ItemsCollection[f.B] =

您可以添加一个子类MapFn,将类型成员移回参数以便于MapFn创建。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Scala编译器无法推断Spark lambda函数中的类型

Scala编译器无法正确推断类型

通用lambda的编译器推断类型

如何在Scala中将依赖于路径的类型与类型类一起使用

为什么Scala编译器不使用抽象类型来推断类型

在Scala中将抽象类型与类型类一起使用

编译器如何从LAMBDA表达式中推断委托类型?

在Scala中指定Lambda返回类型

在scala中将类型布尔类型转换为Int类型

Scala编译器使用复杂类型参数推断出过分具体的类型

在scala中将理解类型转换为结果

Scala类型检查使用lambda而不是显式函数抱怨

故障保险withFallback():为什么kotlin编译器无法推断lambda类型?

在scala中将`Any`类型转换为`Map`类型

在Scala中将任何类型的数组包装为数组类型

为什么在我编写PairDStreamFunctions.reduceByKey时Scala编译器无法推断类型

处理部分应用的函数时,Scala编译器中类型推断限制的原因

Scala错误类型推断

如何将继承与更高级的类型一起使用?

Scala中的lambda类型是什么,它们有什么好处?

在Scala中部分应用的Lambda类型的投影仪

声明参数类型时,Scala Lambda函数无法解析?

为什么Scala不会将此类型Lambda与基础类型统一?

关于scala类型推断的编译错误

Scala 2.11.8类型标记不一定总是由编译器正确生成

编译器是否为每个lambda生成不同的类型?

如何在assertThat中将hamcrest nullValue与类型推断一起使用

Scala编译器有哪些特殊类型?

在Scala中将方法参数限制为特定类型