简体   繁体   English

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

[英]How can I improve Scala's type inference with type parameters that don't show up in the first parameter list?

To illustrate my point, here an example: 为了说明我的观点,这里有一个例子:

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

  protected def someCondition: Boolean

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

}

I'm trying to add a fold method based on an arbitrary condition defined on a wrapped type A . 我正在尝试添加基于在包装类型A上定义的任意条件的fold方法。 The problem with the code above is that this wouldn't compile, although it could conceivably return Any : 上面的代码的问题是,这不会编译,虽然它可以想象返回Any

wrapper.fold("hi")(42)

because by the time the compiler reaches the second parameter list, B has already been inferred to be String . 因为在编译器到达第二个参数列表时, B已被推断为String Suppose we don't want to have to write the type annotation. 假设我们不想编写类型注释。 We can try changing fold to this: 我们可以尝试将fold更改为:

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

but this also doesn't work, since B0 has already been resolved as String at the end of the first parameter list, although it doesn't appear in it at all! 但是这行不通,因为B0已经在第一个参数列表的末尾被解析为String ,尽管它根本没有出现在它中! The simple solution, of course, is to have a single parameter list, but f the sake of the example, let's say I want to keep the two parameter lists and try to make it work… Ideally, we should be able to delay the resolution of B0 . 当然,简单的解决方案是使用单个参数列表,但是为了示例,假设我想保留两个参数列表并尝试使其工作...理想情况下,我们应该能够延迟分辨率B0 It would be great if we could write something like this: 如果我们能写出这样的东西会很棒:

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

But unfortunately this doesn't work. 但不幸的是,这不起作用。 Are there any workarounds? 有没有解决方法?

(I'm providing a first answer, but I'm of course looking for other workarounds as well.) (我提供了第一个答案,但我当然也在寻找其他解决方法。)

It seems that you want to mimic, thanks to compilation, the behavior and the goal of the Either class. 由于编译,您似乎想要模仿Either类的行为和目标。 What you can do is the following: your fold will return an Either object and get your B0 value from it: 你可以做的是:你的折叠将返回一个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)

}

And let the implicit conversion either2mergeable of the Either class do the work: Either类的either2mergeable的隐式转换完成工作:

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

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

Pro: 优点:

  • The Either structure allow you to directly retrieve A and B types Either结构允许您直接检索A和B类型
  • You can check what part was applied during fold 您可以检查折叠过程中应用的部件

Con: 缺点:

  • You do not obtain the result directly 您没有直接获得结果

One solution is to create a temporary instance of a class that defines an apply method simulating the second parameter list, which has itself the B0 type parameter: 一种解决方案是创建一个类的临时实例,该实例定义模拟第二个参数列表的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
  }
}

Then everything works correctly. 一切正常。 Could we even imagine that def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0 could be interpreted as syntactic sugar for this?… 我们甚至可以想象def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0可以解释为这个语法糖?...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM