如何使用未在第一个参数列表中显示的类型参数来改进Scala的类型推断?

Jea*_*let 6 generics parameters scala type-inference

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

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

  protected def someCondition: Boolean

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

}
Run Code Online (Sandbox Code Playgroud)

我正在尝试添加一个fold基于在包装类型上定义的任意条件的方法A.上面的代码的问题是,这不会编译,虽然它可以想象返回Any:

wrapper.fold("hi")(42)
Run Code Online (Sandbox Code Playgroud)

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

def fold[B, B0 >: B](whenTrue: => B)(whenFalse: => B0): B0
Run Code Online (Sandbox Code Playgroud)

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

def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0
Run Code Online (Sandbox Code Playgroud)

但不幸的是,这不起作用.有没有解决方法?

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

Nic*_*las 5

由于编译,你似乎想模仿Either类的行为和目标.您可以做的是:您的折叠将返回一个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)

}
Run Code Online (Sandbox Code Playgroud)

而让隐式转换either2mergeable中的Either类做的工作:

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

scala> res0.fold(42)("hi").merge
res1: Any = 42
Run Code Online (Sandbox Code Playgroud)

优点:

  • Either结构允许您直接检索A和B类型
  • 您可以检查折叠期间应用的部件

缺点:

  • 您没有直接获得结果


Jea*_*let 3

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

\n\n
abstract class Wrapper[A](wrapped: A) {\n\n  // ... as before...\n\n  def fold[B](whenTrue: => B) = new FoldRequest[B](whenTrue)\n\n  class FoldRequest[B](whenTrue: => B) {\n    def apply[B0 >: B](whenFalse: => B0) =\n      if (someCondition) whenTrue else whenFalse\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后一切正常。我们是否可以想象这def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0可以被解释为语法糖?\xe2\x80\xa6

\n