Scala 类型参数括号

发布于 2024-10-22 16:17:04 字数 779 浏览 2 评论 0原文

我知道 trait Foo[T] 意味着 T 是参数化类型。 但有时我可以看到trait Foo[T1,T2],或trait Foo[T1,T2,R],我找不到任何地方描述里面多种类型的含义类型括号,您能指出这种情况下的用法吗?据我推测, Foo[T1,T2] 只是意味着,它定义了两个类型参数,它不必采用 T1 并返回 T2

当我今天阅读 playframework 文档时,我再次发现自己对这个问题感到困惑。在文档中,它说:

BodyParser[A] 基本上是一个 Iteratee[Array[Byte],A],这意味着 它接收字节块(只要网络浏览器上传一些 data)并计算类型 A 的值作为结果。

这个解释听起来像是,类型括号内的第二个类型参数是返回类型。

我还记得 trait Function2 [-T1, -T2, +R] extends AnyRef 表示一个接受 T1T2 的函数,返回一个R

为什么他们将返回类型放在括号中?这是否意味着括号中的最后一个参数都是返回类型?或者他们只是碰巧为返回类型定义了一个新类型 R ?

I know trait Foo[T] means T is a parametrized type.
But some times I can see trait Foo[T1,T2], or trait Foo[T1,T2,R], I cannot find anywhere describe the meaning of multiple types inside a type bracket, could you please point me the usages in this case? From what I speculate, Foo[T1,T2] just means, it defined two type parameters, it doesn't have to be take a T1 and return a T2.

When I read playframework documentation today, I again found myself confused about this question. In the documentation, it says:

A BodyParser[A] is basically an Iteratee[Array[Byte],A], meaning that
it receives chunks of bytes (as long as the web browser uploads some
data) and computes a value of type A as result.

This explanation sounds like, the second the type parameter inside a type bracket is a return type.

I also remember that trait Function2 [-T1, -T2, +R] extends AnyRef means a function that takes a T1 and T2, return a R.

Why do they put the return type in the bracket? Does it mean all the last parameter in a bracket is a return type? Or they just happened defined a new type R for the return type?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

热情消退 2024-10-29 16:17:04

据我推测,Foo[T1,T2] 只是意味着,它定义了两个类型参数,它不必采用 T1 并返回 T2 >.

类型参数的意思只不过是“我需要任何类型,但我不想知道具体类型是什么”,其中“我”是编写代码的程序员。类型参数可以像任何其他类型(例如 Int、String 或 Complex)一样使用 - 唯一的区别是在使用它们之前它们是未知的。

请参阅类型Map[A, +B]。当你第一次阅读本文时,你无法知道 A 和 B 的用途,因此你必须阅读文档:

从 A 类型的键到 B 类型的值的映射。

它解释了类型及其含义。没有什么需要知道或理解的了。它们只是两种类型。可以像 Map[Key, Value] 那样调用 Map,但在源代码内部,类型参数只有一两个字母会更好。这使得区分类型参数和具体类型变得更容易。

它是指定类型参数含义的文档。如果没有文档,您必须查看来源并自行查找其含义。例如,您必须使用 Function2 [-T1, -T2, +R] 来执行此操作。文档只告诉我们这一点:

有 2 个参数的函数。

好的,我们知道三个类型参数中有两个是函数期望的参数,但是第三个是什么呢?我们看一下来源:

def apply(v1: T1, v2: T2): R

啊,现在我们知道 T1T2 是参数,R 是返回类型。

类型参数也可以在方法签名中找到,例如map:

class List[+A] {
  ..
  def map[B](f: (A) ⇒ B): List[B]
}

这就是将map 与List 一起使用时的样子。 A 可以是任何类型 - 它是列表包含的元素的类型。 B 是另一种任意类型。当您知道 map 的作用时,您就知道 B 的作用。否则你必须先了解地图。 map 需要一个可以将 List 的每个元素转换为另一个元素的函数。因为您知道 A 代表列表的元素,您可以从自己那里派生出 B 必须是 A 转换为的类型。

回答您所有其他问题:这不应该通过单一答案来完成。 StackOverflow 上还有很多其他问题和解答,也可以解答您的问题。

总结

当您看到例如 Foo[T1, T2] 中的某些类型参数时,您不应该开始哭泣。想一想:“好吧,我有一个 Foo,它需要 T1 和 T2,如果我想知道它们做什么,我必须阅读文档或源代码。”

From what I speculate, Foo[T1,T2] just means, it defined two type parameters, it doesn't have to be take a T1 and return a T2.

A type parameter means nothing more than "I need any type but I'm not interested to know what it is for a concrete type", where 'I' is the programmer who writes the code. Type parameters can used like any other types such as Int, String or Complex - the only difference is that they are not known until one uses them.

See type Map[A, +B]. When you first read this, you can't know what the A and B are for, thus you have to read the documentation:

A map from keys of type A to values of type B.

It explains the types and their meaning. There is nothing more to know or understand. They are just two types. It is possible to call Map something like Map[Key, Value] but inside of source code it is better when type parameters have only one or two letters. This makes it easier to differ between the type parameters and concrete types.

It is the documentation which specifies what a type parameter means. And if there is no documentation you have to take a look to the sources and find their meaning by yourself. For example you have to do this with Function2 [-T1, -T2, +R]. The documentation tells us only this:

A function of 2 parameters.

Ok, we know that two of the three type parameters are parameters the function expects, but what is the third one? We take a look to the sources:

def apply(v1: T1, v2: T2): R

Ah, now we know that T1 and T2 are the parameters and that R is a return type.

Type parameters also can be found in method signatures like map:

class List[+A] {
  ..
  def map[B](f: (A) ⇒ B): List[B]
}

This is how map looks like when you use it with a List. A can be any type - it is the type of the elements a list contains. B is another arbitrary type. When you know what map does then you know what B does. Otherwise you have to understand map before. map expects a function which can transform each element of a List to another element. Because you know that A stands for the elements of the List you can derive from yourself that B have to be the type A is transformed to.

To answer all your other questions: This shouldn't be done in a single answer. There are a lot of other questions and answers on StackOverflow which can also answer your questions.

Summary

When you see some type parameters for example in Foo[T1, T2] you should not start to cry. Think: "Ok, I have a Foo which expects a T1 and a T2 and if I want to know what they do I have to read documentation or the sources."

十二 2024-10-29 16:17:04

类型括号内的多个类型意味着多个类型的类型参数化。举个例子,

trait Pair[A, B]

这是一对值,一个具有 A 类型,另一个具有 B 类型。

更新:

我认为您对类型参数的语义解释过多。由多个参数参数化的类型就是这样,仅此而已。特定类型参数在类型参数列表中的位置并不使其具有任何特殊性。具体来说,类型参数列表中的最后一个参数不需要代表“返回类型”。

您引用的播放框架中的句子解释了这一特定类型的类型参数的语义。它不能推广到其他类型。对于 Function 类型也是如此:这里最后一个类型参数恰好意味着“返回类型”。但其他类型的情况不一定如此。上面的 Pair[A, B] 类型就是这样一个例子。这里 B 是该对中第二个组件的类型。这里根本没有“返回类型”的概念。

参数化类型的类型参数可以出现在参数化类型定义内“常规”类型可能出现的任何位置。也就是说,类型参数只是类型的名称,仅在实例化参数化类型本身时才绑定到实际类型。

考虑 Tuple 类的以下定义:

class Tuple[A, B](a: A, b: B)

它被实例化为 Int 和 String 的元组类型,如下所示:

type TupleIntString = Tuple[Int, String]

相同,

class TupleIntString(a: Int, b: String)     

这本质上与官方源代码 请检查 Scala 语言规范。具体来说,第 3.4 节“基本类型和成员定义”下的 1. 第 4 个要点说:“参数化类型 C[T_1, ..., T_n] 的基本类型是类型 C 的基本类型,其中每次出现 a C 的类型参数 a_i 已替换为相应的参数类型 T_i。”

Multiple types inside a type bracket means type parametrization on multiple types. Take for example

trait Pair[A, B]

This is a pair of values one having type A the other having type B.

Update:

I think you are interpreting too much into the semantics of type parameters. A type parametrized by multiple parameters is just that and nothing more. The position of a specific type parameter in the list of type parameters does not make it special in any way. Specifically the last parameter in a list of type parameters does not need to stand for 'the return type'.

The sentence from the play framework which you quoted explains the semantics of the type parameters for this one specific type. It does not generalize to other types. The same holds for the Function types: here the last type parameter happens to mean 'the return type'. This is not necessarily the case for other types though. The type Pair[A, B] from above is such an example. Here B is the type of the second component of the pair. There is no notion of a 'return type' here at all.

Type parameters of a parametrized type can appear anywhere inside the definition of the parametrized type where a 'regular' type could appear. That is, type parameters are just names for types which are bound to the actual types only when the parametrized type itself is instantiated.

Consider the following definition of a class Tuple:

class Tuple[A, B](a: A, b: B)

It is instantiated to a type of a tuple of Int and String like this:

type TupleIntString = Tuple[Int, String]

Which is essentially the same as

class TupleIntString(a: Int, b: String)     

For an official source check the Scala Language Specification. Specifically Section 3.4 "Base Types and Member Definitions" under 1. the 4th bullet point says: "The base types of a parameterized type C[T_1, ..., T_n] are the base types of type C , where every occurrence of a type parameter a_i of C has been replaced by the corresponding parameter type T_i."

兮子 2024-10-29 16:17:04

我认为你的问题实际上可以分为三个单独的问题:

类/特征/等的多个类型参数是什么。 ?

一个典型的例子是从一种类型的对象到另一种对象的映射。如果您希望键的类型与值的类型不同,但保持两者通用,则需要两个类型参数。因此,Map[A,B] 采用泛型类型 A 的键并映射到泛型类型 B 的值。想要从书签到页面的映射的用户会将其声明为 Map[Bookmark, Page]。仅具有一种类型参数将不允许这种区别。

据我推测,Foo[T1,T2] 只是意味着,它定义了两个类型参数,它不必采用 T1 并返回 T2。

不,所有类型参数都是平等的公民,尽管它们对于函数对象在这个方向上具有含义。见下文。

这些 +/- 是什么?

它们限制类型参数可以绑定的内容。 Scala 示例 教程有很好的解释。请参阅第 8.2 节方差注释。

scala 中的函数是什么?

为什么他们把返回类型放在括号里?是不是意味着所有的
括号中的最后一个参数是返回类型?或者他们只是发生了
为返回类型定义了一个新类型 R?

Scala 示例教程在第 8.6 节函数中对此进行了很好的解释。

I think your question can actually be broken in three separate problems:

What's with the multiple type parameters for classes/traits/etc. ?

A classic example is a map from one type of object to another. If you want the type for the keys to be different from type of the value, but keep both generic, you need two type parameters. So, a Map[A,B] takes keys of generic type A and maps to values of generic type B. A user that wants a map from Bookmarks to Pages would declare it as Map[Bookmark, Page]. Having only one type parameters would not allow this distinction.

From what I speculate, Foo[T1,T2] just means, it defined two type parameters, it doesn't have to be take a T1 and return a T2.

No, all type parameters are equal citizens, though they have a meaning in that direction for function objects. See below.

What are all those +/-'s ?

They limit what the type parameters can bind to. The Scala by Example tutorial has a good explanation. See Section 8.2 Variance Annotations.

What is a function in scala?

Why do they put the return type in the bracket? Does it mean all the
last parameter in a bracket is a return type? Or they just happened
defined a new type R for the return type?

The Scala by Example tutorial explains this well in Section 8.6 Functions.

百善笑为先 2024-10-29 16:17:04

它们的作用有点类似于类中的作用(即多类型参数),因为特征毕竟是类(没有任何构造函数),旨在作为混合添加到其他类中。

Scala 规范 给出了以下具有多个参数的 Trait 示例:

考虑一个抽象类 Table,它实现从键 A 类型到值 B 类型的映射。
该类有一个方法设置为将新的键/值对输入到表中,还有一个方法 get 返回与给定键匹配的可选值。
最后,还有一个 apply 方法,它与 get 类似,但如果表未针对给定键定义,则它返回给定的默认值。该类的实现如下。

abstract class Table[A, B](defaultValue: B) {
  def get(key: A): Option[B]
  def set(key: A, value: B)
  def apply(key: A) = get(key) match {
    case Some(value) => value
    case None => defaultValue
  }
}

这是Table类的具体实现。

class ListTable[A, B](defaultValue: B) extends Table[A, B](defaultValue) {
  private var elems: List[(A, B)]
  def get(key: A) = elems.find(._1.==(key)).map(._2)
  def set(key: A, value: B) = { elems = (key, value) :: elems }
}

这是一个阻止并发访问其 get 和 set 操作的特征
父类

trait Synchronized Table[A, B] extends Table[A, B] {
  abstract override def get(key: A): B =
  synchronized { super.get(key) }
  abstract override def set((key: A, value: B) =
    synchronized { super.set(key, value) }
}

请注意,SynchronizedTable 不会将参数传递给其超类 Table
即使 Table 是用形式参数定义的。
另请注意,SynchronizedTable 的 get 和 set 方法中的 super 调用静态引用类 Table 中的抽象方法。这是合法的,只要调用方法被标记为抽象覆盖(第 5.2 节)。

最后,以下 mixin 组合创建了一个同步列表
字符串作为键,整数作为值,默认值为 0:

object MyTable extends ListTable[String, Int](0) with SynchronizedTable

对象MyTableSynchronizedTable继承其get和set方法。
这些方法中的 super 调用被重新绑定以引用 ListTable 中的相应实现,这是 SynchronizedTable 中的实际超类型
我的表

Their role is a bit similar to the ones (i.e. multiple type parameter) in a class, since traits are, after all, classes (without any constructor) meant to be added to some other class as a mixin.

The Scala spec gives the following example for Trait with multiple parameters:

Consider an abstract class Table that implements maps from a type of keys A to a type of values B.
The class has a method set to enter a new key/value pair into the table, and a method get that returns an optional value matching a given key.
Finally, there is a method apply which is like get, except that it returns a given default value if the table is undefined for the given key. This class is implemented as follows.

abstract class Table[A, B](defaultValue: B) {
  def get(key: A): Option[B]
  def set(key: A, value: B)
  def apply(key: A) = get(key) match {
    case Some(value) => value
    case None => defaultValue
  }
}

Here is a concrete implementation of the Table class.

class ListTable[A, B](defaultValue: B) extends Table[A, B](defaultValue) {
  private var elems: List[(A, B)]
  def get(key: A) = elems.find(._1.==(key)).map(._2)
  def set(key: A, value: B) = { elems = (key, value) :: elems }
}

Here is a trait that prevents concurrent access to the get and set operations of its
parent class

trait Synchronized Table[A, B] extends Table[A, B] {
  abstract override def get(key: A): B =
  synchronized { super.get(key) }
  abstract override def set((key: A, value: B) =
    synchronized { super.set(key, value) }
}

Note that SynchronizedTable does not pass an argument to its superclass, Table,
even though Table is defined with a formal parameter.
Note also that the super calls in SynchronizedTable’s get and set methods statically refer to abstract methods in class Table. This is legal, as long as the calling method is labeled abstract override (§5.2).

Finally, the following mixin composition creates a synchronized list table with
strings as keys and integers as values and with a default value 0:

object MyTable extends ListTable[String, Int](0) with SynchronizedTable

The object MyTable inherits its get and set method from SynchronizedTable.
The super calls in these methods are re-bound to refer to the corresponding implementations in ListTable, which is the actual supertype of SynchronizedTable in
MyTable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文