【问题标题】:Scala Higher Kinded Types for Traits and Method Parameters特征和方法参数的 Scala 高级类型
【发布时间】:2022-12-11 11:17:16
【问题描述】:

我有一个特征定义,它在像这样实例化时包含副作用:

trait MyTrait[F[_]] {
  def func1(param: Param): F[Param]
}

我的 Param 是一个案例类,它本身采用这样的类型参数:

final case class Param[F[_]] {
  field1: F[String],
  field2: F[Int]
)

现在我的问题是,如果我将特征方法签名更改为以下内容,这意味着什么:

trait MyTrait[F[_]] {
  def func1(param: Param[_]): F[Param[_]]
}

如您所见,我在所有引用 Param 案例类的地方都使用了通配符。这是一个好方法吗?我不想将我的接口与方法参数上的类型期望联系起来。

【问题讨论】:

  • final case class Param[F[_]] { ... } 是无效语法。 F[Param] 不是 Param: (* -> *) -> *F: * -> * 的格式正确的类型。我不明白在问什么。做什么“这是个好办法吗?”意思是?接近什么?字体是否表达了你想表达的意思?如果是这样,那是正确的方法。如果没有,那就是错误的方法。不知道你想表达什么就不可能说。

标签: scala generics existential-type higher-kinded-types type-constructor


【解决方案1】:

正如@AndreyTyukin 所注意到的,您的代码无法编译,因为ParamF 类型不一致

trait MyTrait[F[_]] {
  def func1(param: Param): F[Param]
}
//compile error: class Param takes type parameters

final case class Param[F[_]](
  field1: F[String],
  field2: F[Int]
)

https://scastie.scala-lang.org/DmytroMitin/K2EHGDXERFCJisz45edMsA

也许你的意思是

trait MyTrait[F[_]] {
  def func1(param: Param[F]): F[Param[F]]
}

final case class Param[F[_]](
  field1: F[String],
  field2: F[Int]
)

https://scastie.scala-lang.org/DmytroMitin/K2EHGDXERFCJisz45edMsA/1

func1返回类型F[Param[F]]看起来像修复点https://free.cofree.io/2017/11/13/recursion/

现在我的问题是,如果我将特征方法签名更改为以下内容,这意味着什么:

trait MyTrait[F[_]] {
  def func1(param: Param[_]): F[Param[_]]
}

您开始使用具有任意(未知)效果的存在类型Param[_],而不是具有当前效果的Param[F]F,可能与F不同。

What is an existential type?

这是一个好方法吗?我不想将我的接口与方法参数上的类型期望联系起来。

取决于你的目标。 MyTraitParam 将具有未连接的效果对您的设置有意义吗?

例如,其中一个正在访问数据库,而另一个正在写入磁盘上的文件。其中一个正在穿越时空,而另一个正在发射导弹。

如果这对您的设置使用不同的效果确实有意义,请考虑修改签名,在方法级别添加第二个效果类型(而不是存在的)

trait MyTrait[F[_]] {
  def func1[G[_]](param: Param[G]): F[Param[G]]
}

(还是应该仍然是F[Param[F]]?还是G[Param[F]]?这取决于您的设置。)

或在类型级别

// (*)
trait MyTrait[F[_], G[_]] {
  def func1(param: Param[G]): F[Param[G]]
}

或者你甚至可以尝试

trait MyTrait[F[_]] {
  def func1[G[_], H[_]](param: Param[G]): F[Param[H]]
}

或者

trait MyTrait[F[_], G[_], H[_]] {
  def func1(param: Param[G]): F[Param[H]]
}

在新问题Type Arguments and Bounds in Scala 之后,我将在此处添加一个选项,即使G 成为抽象类型成员而不是方法的类型参数。然后 G 必须在继承者中实现,而不是该方法必须适用于任意 G

trait MyTrait[F[_]] {
  type G[_]
  def func1(param: Param[G]): F[Param[G]]
}

它类似于上面的 (*),即具有 G 类型类的类型参数。

【讨论】:

  • Idk,我觉得 Param[_] 和更亲切的 _ 是 99.99% 的危险信号,除非 OP 秘密地是 Miles Sabin 或其他人......人们多久想要对效果进行存在量化?甚至可以做出什么有意义的陈述,对影响进行存在量化?“其中一个在穿越时空,另一个在发射导弹”- 听起来像是一个重要的选择,可能太重要了,不能不加说明或埋在通配符中。
  • @AndreyTyukin 好吧,更高级的存在主义不会威胁到我github.com/apache/spark/pull/38740/… 虽然也许你是对的。请参阅第 2 种效果类型的更新。
  • “高等存在主义不会威胁我”- 我毫不怀疑他们不会威胁你,但是当我看到问题中的非编译代码时,我觉得其他人的情况可能会略有不同?我现在觉得你的回答已经涵盖了某种所有可编译解决方案空间中的超球体与问题中代码的最小 Levenshtein 距离,一方面可能大大简化了 OP 的生命,但另一方面表明问题有点XY 问题。
  • 我确实尝试过玩,这是一个编译失败的版本:scastie.scala-lang.org/NvvgWEYMTKuDVXPfQgbRVQ
猜你喜欢
  • 2023-04-01
  • 2019-10-07
  • 2023-04-03
  • 2013-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2023-03-30
相关资源
最近更新 更多