这里有人说星号是 scala 3 的下划线,但我在 scala 2.13 中看到过这样的代码:
def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
它是否具有相同的含义,只是指定 * 中的类型与 _ 中的类型不同?
这里有人说星号是 scala 3 的下划线,但我在 scala 2.13 中看到过这样的代码:
def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
它是否具有相同的含义,只是指定 * 中的类型与 _ 中的类型不同?
_
表示(取决于上下文)
def foo[F[_]]: Unit
def bar(f: F[_]): F[_]
这里我们要了解类型构造函数。
类型构造函数将是(简化)F
某事物的构造函数,尚未定义该事物,但我们可以将其应用于A
它并使其成为F[A]
. 例如
List
可以通过,F[_]
因为它有一个间隙,如果我们用 eg 填充它,String
它可能变成List[String]
Option
也可以通过F[_]
,它有一个间隙,如果我们用 eg 填充它,Int
它会变成Option[Int]
Double
不能用作 as F[_]
,因为它没有间隙带有“间隙”的类型通常表示为* -> *
,而没有“间隙”的类型通常表示为*
。我们可以*
简单地将其理解为一种类型,而* -> *
将其理解为“采用另一种类型来形成类型的类型”——或类型构造函数。
(像刚才提到的那种高级类型本身就是复杂的东西,所以你最好在这个问题之外更多地了解它们)。
*
(来自kind 投影仪插件)用于 kind 投影 - 语法的灵感来自上面的符号,以显示如果我们想创建一个新类型,类型将被传递到哪里:
Monad[F[List[*]]]
真的很像:
type UsefulAlias[A] = F[List[A]]
Monad[UsefulAlias]
除了它在没有类型别名的情况下工作。
如果是 Dotty,最好用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]
同时善良的*
应该是* -> *
而不是*
。所以这*[_]
意味着“我们想在这里创建一个新的类型构造函数,用这个东西代替*
一个参数,但是我们想表示这个参数是 kind* -> *
而不是*
[F[_]] =>> MonadError[F, Throwable]
所以我们将添加[_]
以向编译器显示它是一个类型构造函数。
吸收的比较多,应该会比较容易,只能不好意思说,在Dotty里面会更清晰一些。