类型参数中 *(星号)和 _(下划线)的区别
Difference between * (star) and _ (underscore) in type parameter
有人说星号是 scala 3 的下划线,但我在 scala 2.13 中看到过这样的代码:
def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
是不是同一个意思,只是说明*中的类型与_中的类型不同?
_
表示(取决于上下文)
- 类型构造函数 - 如果用作类型参数 definition/constraint
def foo[F[_]]: Unit
- 存在类型 - 如果应用于应作为正确类型使用的事物
def bar(f: F[_]): F[_]
这里我们要了解一下类型构造函数
类型构造函数将是(简化)F
的东西,还没有定义它,但我们可以将 A
应用到它并使它成为 F[A]
.例如
List
可以作为 F[_]
传递,因为它有一个间隙,如果我们用例如填充它String
可能会变成 List[String]
Option
也可以作为 F[_]
传递,它有一个间隙,如果我们用例如填充它Int
会变成 Option[Int]
Double
不能用作F[_]
,因为它没有间隙
带有 "gap" 的类型通常表示为 * -> *
,而没有它们的类型通常表示为 *
。我们可以将 *
简单地理解为一种类型,而将 * -> *
理解为 "type that takes another type to form a type" - 或类型构造函数。
(像刚才提到的高亲类类型本身就是复杂的东西,所以你最好在那个问题之外更多地了解它们)。
*
(来自 kind projector plugin)用于种类投影——语法的灵感来自上面的符号,以显示如果我们想创建一个新类型,类型将传递到哪里:
Monad[F[List[*]]]
真像:
type UsefulAlias[A] = F[List[A]]
Monad[UsefulAlias]
除了它在没有类型别名的情况下工作。
如果是Dotty,可以用type lambda更好的表达:
// Monad[F[List[*]]] is equal to
[A] =>> Monad[List[A]]
在你的例子中:
def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
F[_]
被定义为类型构造函数 - 所以你不能传递 String
、Int
或 Byte
,但你可以传递 List
, Future
或 Option
(因为它们采用一种类型参数)
F[_]: ContextShift
是 [F[_]](implicit sth: ContextShift[F])
的快捷方式——我们可以看到 ContextShift
将一些自身带有类型参数的东西作为参数(比如 F[_]
)
[F[_]: MonadError[*[_], Throwable]
可以扩展为:
type Helper[G[_]] = MonadError[G, Throwable]
[F[_]: Helper]
这又可以重写为
type Helper[G[_]] = MonadError[G, Throwable]
[F[_]](implicit me: Helper[F])
或者使用类型 lambda
[F[_]] =>> MonadError[F, Throwable]
如果写成这样可能会更容易阅读:
def make[F[_]: ContextShift: MonadError[*, Throwable]: Effect: Logging]():
事实是,*
表明预期类型是
[A] =>> MonadError[A, Throwable]
同时 *
的善意应该是 * -> *
而不是 *
。所以这个 *[_]
的意思是“我们想在这里创建一个新的类型构造函数,用这个东西代替 *
一个参数,但是我们想表示这个参数是 * -> *
类型的*
[F[_]] =>> MonadError[F, Throwable]
所以我们将添加 [_]
以向编译器显示它是一个类型构造函数。
要吸收的东西还不少,应该会比较容易,只能遗憾地说,在Dotty里会更清楚。
def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
是不是同一个意思,只是说明*中的类型与_中的类型不同?
_
表示(取决于上下文)
- 类型构造函数 - 如果用作类型参数 definition/constraint
def foo[F[_]]: Unit
- 存在类型 - 如果应用于应作为正确类型使用的事物
def bar(f: F[_]): F[_]
这里我们要了解一下类型构造函数
类型构造函数将是(简化)F
的东西,还没有定义它,但我们可以将 A
应用到它并使它成为 F[A]
.例如
List
可以作为F[_]
传递,因为它有一个间隙,如果我们用例如填充它String
可能会变成List[String]
Option
也可以作为F[_]
传递,它有一个间隙,如果我们用例如填充它Int
会变成Option[Int]
Double
不能用作F[_]
,因为它没有间隙
带有 "gap" 的类型通常表示为 * -> *
,而没有它们的类型通常表示为 *
。我们可以将 *
简单地理解为一种类型,而将 * -> *
理解为 "type that takes another type to form a type" - 或类型构造函数。
(像刚才提到的高亲类类型本身就是复杂的东西,所以你最好在那个问题之外更多地了解它们)。
*
(来自 kind projector plugin)用于种类投影——语法的灵感来自上面的符号,以显示如果我们想创建一个新类型,类型将传递到哪里:
Monad[F[List[*]]]
真像:
type UsefulAlias[A] = F[List[A]]
Monad[UsefulAlias]
除了它在没有类型别名的情况下工作。
如果是Dotty,可以用type lambda更好的表达:
// Monad[F[List[*]]] is equal to
[A] =>> Monad[List[A]]
在你的例子中:
def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
F[_]
被定义为类型构造函数 - 所以你不能传递String
、Int
或Byte
,但你可以传递List
,Future
或Option
(因为它们采用一种类型参数)F[_]: ContextShift
是[F[_]](implicit sth: ContextShift[F])
的快捷方式——我们可以看到ContextShift
将一些自身带有类型参数的东西作为参数(比如F[_]
)[F[_]: MonadError[*[_], Throwable]
可以扩展为:
这又可以重写为type Helper[G[_]] = MonadError[G, Throwable] [F[_]: Helper]
或者使用类型 lambdatype Helper[G[_]] = MonadError[G, Throwable] [F[_]](implicit me: Helper[F])
[F[_]] =>> MonadError[F, Throwable]
如果写成这样可能会更容易阅读:
def make[F[_]: ContextShift: MonadError[*, Throwable]: Effect: Logging]():
事实是,*
表明预期类型是
[A] =>> MonadError[A, Throwable]
同时 *
的善意应该是 * -> *
而不是 *
。所以这个 *[_]
的意思是“我们想在这里创建一个新的类型构造函数,用这个东西代替 *
一个参数,但是我们想表示这个参数是 * -> *
类型的*
[F[_]] =>> MonadError[F, Throwable]
所以我们将添加 [_]
以向编译器显示它是一个类型构造函数。
要吸收的东西还不少,应该会比较容易,只能遗憾地说,在Dotty里会更清楚。