类型参数中 *(星号)和 _(下划线)之间的差异



这里有人说 star 是 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 投影器插件)用于 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[_]被定义为类型构造函数 - 所以你不能传递StringIntByte,但你可以传递到那里ListFutureOption(因为它们接受一个类型参数)
  • 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 会更清楚。

相关内容

  • 没有找到相关文章

最新更新