_ 表示(取决于上下文)
- 类型构造函数 - 如果用作类型参数定义/约束
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[_],因为它没有间隙
带有“间隙”的类型通常表示为* -> *,而没有间隙的类型通常表示为*。我们可以将* 简单地理解为一种类型,而将* -> * 理解为“采用另一种类型形成类型的类型”——或类型构造函数。
(像刚才提到的那种高级类型本身就是复杂的东西,所以你最好在这个问题之外更多地了解它们。
*(来自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里会更清晰。