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

问题描述 投票:0回答:1

[Here有人说星标是scala 3的下划线,但是我在scala 2.13中看到了这样的代码:

def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...

它具有相同的含义,只是在*中指定的类型与_中的类型不同吗?

scala functional-programming scala-cats
1个回答
1
投票

[_表示(取决于上下文)] >>

  • 类型构造函数-如果在类型参数定义/约束中使用
    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[_]被定义为类型构造函数-因此您无法在其中传递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]

所以我们将添加[_]以向编译器显示它是类型构造函数。

[它吸收了很多东西,应该会更容易,我只能感到抱歉,并说在《多蒂》中它会更清楚。

© www.soinside.com 2019 - 2024. All rights reserved.