【问题标题】:Scala requires String: Question about Scala genericsScala 需要字符串:关于 Scala 泛型的问题
【发布时间】:2020-08-26 03:43:57
【问题描述】:

我正在关注 Paul Chiusano 和 Rúnar Bjarnason 的出色 Functional Programming in Scala,并且对我发现的奇怪/意外行为提出了疑问。

我已经像这样定义了一个foldRight 函数

def foldRight[A,B](l: List[A], z: B)(f: (A,B) => B): B = 
  l match {
    case Nil => z
    case x :: xs => f(x, foldRight(xs, z)(f)) 
  }

只要我传入具体的参数,它就可以正常工作,例如:

foldRight((1 to 10).toList, 0)(_ + _)
val res6: Int = 55

如果我定义一个采用通用列表的类似函数

def sum[A](as: List[A]): Int = foldRight(as, 0)(_ + _)

奇怪的事情发生了

  def sum[A](as: List[A]): Int = foldRight(as, 0)(_ + _)
                                                      ^
fold.scala:23: error: type mismatch;
 found   : Int
 required: String

最初,我对这个错误消息感到非常困惑,因为唯一的类型是ABInt。然而,它似乎只是试图通过调用.toString 来理解+ 和通用A,就像我read about here 一样。

如果是这样的话,为什么不简单地添加 a1.toString + a2.toString + ... + a n.toString + 0 = something0?编译器完全理解 Scala 中 StringInt 之间的字符串连接。

希望有人可以帮助澄清这里发生的事情。

【问题讨论】:

  • 您希望如何汇总通用A 的列表?如果我给您 UsersMapsList 怎么办?这应该让你知道什么是错的。现在,对于特定的错误消息(_ + _) 扩展为(x: A, y: Int) => x + y,因为我们不知道 A 的任何内容,所以我们可以假设它具有与 Any 相同的方法b>,它确实有一个+ 方法(由于该死的Java,它也接受一个Any并返回一个String。所以x + y 给出了一个String,但编译器期望来自(A, Int) => Int 的函数,因此出现错误。
  • 此时,您根本无法编写该函数。稍后,您将了解 typclasses,并且您将能够根据表现为 Number 的任何 A 来编写此函数。跨度>
  • 超级有用和明确的答案,谢谢你,路易斯!
  • 查看scala-lang.org/api/current/scala/Any.html并通过继承过滤成员以找到+方法
  • @Saskia 从技术上讲没有。实际上是的,因为任何东西都必须具有 Any 的所有方法(但它们可能会抛出异常,例如 null,或者您可能永远无法获得调用方法的实际值,就像 Nothing)。另外,我相信从 AAny 的隐式转换总是在范围内。

标签: scala generics functional-programming fold


【解决方案1】:

但是,它似乎只是试图通过调用 .toString 来理解 + 和泛型 A

这不是发生的事情。结果相似,但它没有调用.toString。不过,您发现了根本问题。

sum 函数对您要在其上调用+ 方法的通用A 进行操作。

但是 A 没有 + 方法(请记住,中缀位置的 +x.+(y) 相同)。然后编译器在 隐式作用域 中搜索函数或类构造函数,以将此 A 转换为具有 + 方法的东西。它在any2stringadd 中找到它。

你的方法实际上看起来像

def sum[A](as: List[A]): Int = foldRight(as, 0)(any2Stringadd(_) + _)

现在这个错误是有道理的。因为 any2Stringadd 类的 + 方法需要一个字符串作为它的参数。但是您的 z 参数是 Int 类型。您可以看到,当您将类型显式添加到内联函数参数时。

正如其他人指出的那样,如果不限制类型参数,这是不可调和的。

【讨论】:

    【解决方案2】:

    正如其他人所说,您不能尝试添加您一无所知的 A 类型的实例,因为 Scala 编译器会感到困惑并试图将您的 A 对象隐式转换为字符串,即使您有 0 , 这是一个 Int。但是,您可以像这样使用类型类来解决这个问题。

    trait Sum[T] {
      def id: T
      def add(_1: T, _2: T): T
    }
    
    implicit object IntSum extends Sum[Int] {
      def id = 0
      def add(i1: Int, i2: Int) = i1 + i2
    }
    
    def sum[A](as: List[A])(implicit sum: Sum[A]): A = foldRight(as, sum.id)(sum.add)
    

    这里,IntSum 被隐式传递给 sum 方法,它的 add 方法用于将您的列表相加。您可以为DoubleString 等定义任意数量的此类隐式对象。

    【讨论】:

      【解决方案3】:

      让我们通过思考它们应该做什么来正确命名一些函数:

      def foldRight[A,B](l: List[A], z: B)(f: (A,B) => B): B =
        l match {
          case Nil => z
          case x :: xs => f(x, foldRight(xs, z)(f))
        }
      
      def sumInts(numbers: List[Int]): Int = foldRight(numbers, 0)(_ + _)
      
      def concatStrings(strings: List[String]): String = foldRight(strings, "")(_ + _)
      
      def concatStringRepresentations[A](as: List[A]): String = foldRight(as, "")(_ + _)
      concatStringRepresentations(List(List(), List(1,2,3), List(0))) // List()List(1, 2, 3)List(0)
      concatStringRepresentations(List(1.0, 2.0, 3.0)) // 1.02.03.0
      
      def sumIntRepresentations[A](as: List[A]): Int = foldRight(as, 0)(_ + _)  
      // --> type mismatch, found: String, required: Int, for second param of (_ + _)
      

      这可能是您的sum 函数应该命名的名称。 为了让它编译,我们希望有

      • 为任何类型定义的方法+: Int => Int,或者...
      • 将任何类型转换为Int 的隐式转换(类似于toStringInt),或者...
      • 其他有意义的隐式转换,或者...
      • 其他东西,可以使这个函数成功并被编译器识别。

      事实证明,Any 可以隐式转换为具有 +(other: String) => String 方法的东西,该方法使 String 示例工作,但没有从 Any 到具有方法 @987654331 的东西的隐式转换@。

      这可以更详细地解释,其他答案已经这样做了。

      意见:

      所以,是的,由于存在隐式转换,编译器会给出一条令人困惑的消息。但是让我们看看手头的问题,这可能是我们的一厢情愿。有时东西就像在concatStringRepresentations 函数中一样工作。它通常不起作用,但在这种特殊情况下 String 它可以。

      有时某些东西适用于特殊情况。而当我们看不到它是一个特殊情况时,就会发生错误,例如当我们将其应用于其他事物时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-17
        • 1970-01-01
        • 2013-06-26
        相关资源
        最近更新 更多