[Here有人说星标是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[_]
传递,因为如果有空格,例如用String
可能会变成List[String]
Option
也可以作为F[_]
传递,如果我们用例如Int
它会变成Option[Int]
Double
不能用作F[_]
,因为它没有空格具有“空白”的类型通常表示为* -> *
,而没有它们的类型通常表示为*
。我们可以简单地将*
读作一个类型,而将* -> *
读为“需要另一个类型构成一个类型的类型”。
(种类繁多的类型本身就是复杂的东西,因此,您最好在该问题之外进一步了解它们。
[*
(来自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]
*
的友善度应为* -> *
,而不是*
。因此,此*[_]
的意思是“我们要在此处创建一个新的类型构造函数,以代替*
作为参数,但我们要表示此参数属于* -> *
类型,而不是*
[F[_]] =>> MonadError[F, Throwable]
所以我们将添加
[_]
以向编译器显示它是类型构造函数。
[它吸收了很多东西,应该会更容易,我只能感到抱歉,并说在《多蒂》中它会更清楚。