在scala中键入参数

时间:2016-09-02 02:40:18

标签: scala

我试图理解scala中的类型参数概念。

$ source ~/.profile
$ rake db:create
$ rake db:migrate

下面:

def sum [A] (a:A):A={a}
// used single parameter and its working fine, able to pass any data type.

上面的函数有一个类型参数,我没有提到任何数据类型 - 那么它如何被视为字符串?

2 个答案:

答案 0 :(得分:4)

def sum[A](a:A,b:A):A={a+b}

您要添加T + T,编译器无法推断+ T,它将使用默认implicit + }:any2stringadd,因此编译器会抛出required: String错误。

请参阅https://issues.scala-lang.org/browse/SI-8229

  implicit final class any2stringadd[A](private val self: A) extends AnyVal {
    def +(other: String): String = String.valueOf(self) + other
  }

答案 1 :(得分:3)

让我们从一个不同的角度来看待这个:实际上,一个参数是什么?这是什么意思?

让我们从你可能更熟悉的东西开始:值参数。子程序中的值参数是什么意思?说,我们有这样的事情:

def foo(a, b) = { /* … */ } // deliberately ignoring types and the body

这意味着:我有一个包含两个参数fooa的子例程b。子例程是 general ,它不关心ab实际具体值是什么。它适用于ab所有值,因为它甚至知道 a和{{的值1}}是。

更具体一点:

b

同样,def plus(a: Int, b: Int) = a + b 不知道plusa的实际值是什么。它适用于b2以及323420。它完全无视0a的具体价值观。它只知道ba存在。

现在,类型参数是相同的,只是在类型级别而不是值级别。

b

在类型级别意味着同样的事情,blabla[A] 意味着在价值层面:“我有一个东西,我不知道它是什么(我不在乎),但我会打电话它blabla(a)。“

所以,你有效地做了什么,就是告诉Scala你对A一无所知。但是你用它做了一些事情(或者更确切地说是它的实例):你添加它。但是,你怎么知道你可以添加它?你甚至都不知道它是什么!

所以,添加无法可能正常工作,必须失败!

然而,它失败的方式有点奇怪,并且与scala.Predef对象中的一些预定义的隐式转换有关。特别是there is an implicit conversion for string concatenation, which can convert an arbitrary object into a String and then concatenate another String to it。在这种情况下,它会将A转换为any2stringadd,然后尝试向其添加a,但any2stringadd.+方法仅将b作为参数,因此您会得到一个奇怪的错误消息,即它期待String

当您遇到复杂的类型错误时,有时会弹出其他类似的类型:

  • AnyAnyRefAnyVal:这些类型位于Scala类型层次结构的最顶层。有时候,如果你有一些编程错误,你认为你从两个不同的代码路径返回相同的类型,但实际上你返回两种不同的类型,Scala仍然会尝试推断两者之间的共同类型,并最终得到只有共同的祖先是StringAnyAnyRef。 (这有点像Java或C♯中的AnyVal。)
  • Serializable:这实际上和上面一样。许多完全不同的类型实现了Object接口,因此有时当你有两种非常不同的类型,你实际上期望相同的类型时,Scala会帮助推断Serializable作为最接近的共同祖先,然后大喊大叫你做了Serializable它不支持的东西。
  • Product是所有Serializable的超级特征(即Product1Product2,... Product22),因此所有Product s(即Tuple1Tuple2,... Tuple22)。 Tuple也混合在所有Product es中。所以,如果你有两个case class个不同的arity或两个不相关的Tuple es(例如你有时会返回None但是然后意外地返回case class而不是Some(somethingOfTypeFoo) ),那么OptionsomethingOfTypeFoo之间最精确的常见类型将被推断为case class Foo或...
  • Product:也可以收到上述内容的组合。例如。 Product with SerializableTuple,因此实际上是两个Serializable不同arity中最精确的常见类型。

遇到这些问题的一种常见方法是使用不带Tuple的条件表达式:

else

这是什么类型的回归? Int?没有! if (true) 42 分支返回then,但Int分支返回 nothing (因为没有else分支)。 Scala实际上有一个不返回任何内容的返回类型:UnitelseUnit的子类型,AnyValInt的子类型,因此条件表达式的两个分支的类型最接近的共同祖先实际上是{{ 1}},而不是AnyVal。 (注意:AnyVal分支无法访问的事实与打字角度无关。可达性是一个运行时事物,类型是编译时间。)