【问题标题】:How can I improve Scala's type inference with type parameters that don't show up in the first parameter list?如何使用第一个参数列表中未显示的类型参数改进 Scala 的类型推断?
【发布时间】:2011-08-05 09:52:06
【问题描述】:

为了说明我的观点,这里举个例子:

abstract class Wrapper[A](wrapped: A) {

  protected def someCondition: Boolean

  def fold[B](whenTrue: => B)(whenFalse: => B): B =
    if (someCondition) whenTrue else whenFalse

}

我正在尝试根据包装类型A 上定义的任意条件添加fold 方法。上面代码的问题是它无法编译,尽管可以想象它可以返回Any

wrapper.fold("hi")(42)

因为当编译器到达第二个参数列表时,B 已经被推断为String。假设我们不想编写类型注释。我们可以试试把fold改成这样:

def fold[B, B0 >: B](whenTrue: => B)(whenFalse: => B0): B0

但是这个不起作用,因为B0已经在第一个参数列表的末尾被解析为String,尽管它根本没有出现在其中!简单的解决方案当然是有一个参数列表,但是为了示例,假设我想保留两个参数列表并尝试使其工作......理想情况下,我们应该能够 延迟B0 的分辨率。如果我们能写出这样的东西就好了:

def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0

但不幸的是,这不起作用。有什么解决方法吗?

(我提供的是第一个答案,但我当然也在寻找其他解决方法。)

【问题讨论】:

  • 这看起来确实是一个可以用Eitherclass 稍微不同处理的问题,你考虑过这种可能性吗?
  • @Nicolas 我看不到使用Either 将如何帮助编译器推断参数whenTruewhenFalse 的最具体的常见超类型,但请随时提出答案,我会非常有兴趣阅读它。

标签: generics scala parameters type-inference


【解决方案1】:

由于编译,您似乎想模仿Either 类的行为和目标。 您可以执行以下操作:您的 fold 将返回一个 Either 对象并从中获取您的 B0 值:

abstract class Wrapper[A](wrapped: A) {

  protected def someCondition: Boolean

  def fold[A, B](whenTrue: => B)(whenFalse: => A): Either[A, B] =
    Either.cond(someCondition, whenTrue, whenFalse)

}

Eitherclass 的隐式转换either2mergeable 完成这项工作:

scala> new Wrapper[Unit] {def someCondition = true}
res0: Wrapper[Unit] = $anon$1@77026e40

scala> res0.fold(42)("hi").merge
res1: Any = 42

亲:

  • Either 结构允许您直接检索 A 和 B 类型
  • 您可以检查折叠过程中应用了哪些部分

缺点:

  • 你不是直接得到结果

【讨论】:

  • 有趣。看起来我可以在没有RichEither 技巧的情况下直接调用merge。但这只是部分帮助,因为我仍然需要额外烦人的merge 调用,这实际上只是样板......
  • 是的,我刚刚重新创建了 MergeableEither 和合并功能,感谢您指出,我会调整我的答案。如果你想避免合并,也许implicit def toAnswer[A](e: Either[A, A]) => A是答案?
  • 忘记隐含的想法:只要普通类型是Any,就不行了。
【解决方案2】:

一种解决方案是创建一个类的临时实例,该实例定义一个模拟第二个参数列表的apply 方法,它本身具有B0 类型参数:

abstract class Wrapper[A](wrapped: A) {

  // ... as before...

  def fold[B](whenTrue: => B) = new FoldRequest[B](whenTrue)

  class FoldRequest[B](whenTrue: => B) {
    def apply[B0 >: B](whenFalse: => B0) =
      if (someCondition) whenTrue else whenFalse
  }
}

然后一切正常。我们甚至可以想象def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0 可以被解释为语法糖吗?...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 1970-01-01
    • 2019-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多