【问题标题】:What exactly does def foo[A,B] mean? Type shadowingdef foo[A,B] 到底是什么意思?类型阴影
【发布时间】:2015-02-23 06:02:20
【问题描述】:

当我编写像def foo[A,B] 这样的函数时,[A,B] 到底是什么意思?我知道这是Polymorphic Method;但是你什么时候使用foo [A]foo [A,B]

这是一个我不明白区别的例子。该函数编译:

def map[B](f: A => B): Stream[B] =                                               
  foldRight(empty[B])((h,t) => cons(f(h), t))

而这个没有编译。我不明白为什么不需要A,毕竟Af: A => B 引用:

def map[A,B](f: A => B): Stream[B] =                                            
  foldRight(empty[B])((h,t) => cons(f(h), t))

[error] ..../Stream.scala:61: type mismatch;
[error]  found   : h.type (with underlying type A)
[error]  required: A
[error]     foldRight(empty[B])((h,t) => cons(f(h), t))

(这是来自FP in Scala 练习之一)

附录

阅读答案后,我添加了一些上下文,以帮助未来的读者。该函数是在 trait 中定义的:

trait Stream[+A] { 
  ...
  def map[B](f: A => B):Stream[B] = 
    foldRight(empty[B])((h,t) => cons(f(h), t))
  ...
}

所以错误是由 type shadowing 引起的,但请参阅下面@acjay 的出色回答。

谷歌搜索 scala type shadowing 不会导致任何 scala 文档中的直接定义,这很有趣,因为正如 @travisbrown 下面所说,它是“我见过的最常见的初学者混淆来源之一”。这里有讨论:Why does Scala support shadow variables?

【问题讨论】:

  • 您在哪里看到map 方法?如果它是类的成员,则类本身可能会定义A 类型参数
  • 您在定义Stream[A] 的类中定义map(我假设),因此抽象类型A 已经定义,map 只需要为函数的结果类型。
  • 这意味着您正在定义一个处理您还不知道的两种不同类型的函数,并且由于您不知道它们,因此您将为它们命名(A. B) 作为占位符。您是在告诉编译器,当用户调用函数时,它应该根据使用函数的位置将 A 和 B 替换为正确的类型。
  • 感谢@mucaho 和@Freidereikhs,这确实是流特征的一部分,我会更新问题以添加一些上下文。

标签: scala


【解决方案1】:

在第一个示例中,只有一个类型参数,因为它不是一个孤立的函数,它是类Stream[A] 中的一个方法,它声明了第一个类型参数。大概是这样:

class Stream[A] {
  // ...

  def map[B](f: A => B): Stream[B] =                                               
    foldRight(empty[B])((h,t) => cons(f(h), t))

  // ...
}

所以你从周围的类范围中获得了A。相反,假设您在伴随对象上创建了 map 方法。在这种情况下,伴生对象没有类型参数,因此您必须在方法上定义这两个参数:

class Stream[A] {
  // ... methods, with foldRight, but no `map`
}

object Stream {
  // ...

  def map[A, B](stream: Stream[A])(f: A => B): Stream[B] =                                               
    stream.foldRight(empty[B])((h,t) => cons(f(h), t))

  // ...
}

第二个选项的使用方式略有不同。你会说Stream.map(myStream)(myMappingFunction) 而不是myStream.map(myMappingFunction)。这两个选项都完全有效,但将方法放在类中可能更惯用。

因此,为了回答您的问题,当两个或多个参数和/或返回类型需要是泛型时,您可以在一个方法上使用多个类型参数。您也可以对类使用两个类型参数,例如(忽略方差):

键入Map[A, B] - A 是键类型,B 是值类型

类型Tuple2[A, B](更好地称为(A, B)) - A 是第一个元素的类型,B 是第二个元素的类型

类型Function1[A, B](更好地称为A => B) - A 是参数类型,B 是返回类型

...等等。

【讨论】:

  • 非常感谢@acjay 的出色回答,我现在明白了。我将在我的问题中添加一个附录以提供一些背景信息。
猜你喜欢
  • 2011-03-26
  • 1970-01-01
  • 2017-02-02
  • 2013-12-16
  • 2014-02-09
  • 2012-06-14
  • 2018-08-06
  • 1970-01-01
  • 2022-11-10
相关资源
最近更新 更多