(p, a))) .flatMap { case (p, a-6ren">
gpt4 book ai didi

scala - 传递路径依赖类型无法保留依赖值

转载 作者:行者123 更新时间:2023-12-04 11:21:57 26 4
gpt4 key购买 nike

考虑以下:

trait Platform {
type Arch <: Architecture
def parseArch(str: String): Option[Arch]
}

object Platform {
def parse(str: String): Option[Platform] = ???
}

trait Architecture

def main() {
def exec(p: Platform)(a: p.Arch) = ???

Platform.parse("ios")
.flatMap(p => p.parseArch("arm64").map(a => (p, a)))
.flatMap { case (p, a) => exec(p)(a) } // <----- This fails to compile
}
exec(p)(a)无法编译并显示错误消息:

Error:(17, 40) type mismatch;
found : a.type (with underlying type A$A2.this.Platform#Arch)
required: p.Arch .flatMap { case (p, a) => exec(p)(a) }



从错误消息来看,scalac 似乎无法保留 p 上的值( Arch )。取决于,因此它选择键入投影(尽管我不太确定 A$A2.this 是什么意思)。

对于它的值(value),用以下内容替换最后一行将编译:
.flatMap(p => exec(p)(p.parseArch("arm64").get))

这是 Scala 编译器的限制还是我在这里遗漏了什么?

最佳答案

简单的解决方案

处理依赖于路径的类型时,最好的办法是始终保留所有者值,否则 Scala 的推理和推理能力非常有限。

例如,您的代码示例可以重写为:

Platform.parse("ios") flatMap {
p => p.parseArch("arm64").map(exec(p))
}

尽管代码通常会变得不那么简洁和优雅,但通常可以执行此类重写。通常的做法是使用依赖函数和参数类。

使用依赖类型

在您的示例中,代码:
Platform.parse("ios").flatMap(p => p.parseArch("arm64").map(a => (p, a)))

有类型 Option[(Platform, Platform#Arch)] ,因为 Scala 的推理无法保留元组的第二个元素依赖于第一个元素的事实。 (你得到 A$A2.this.Platform 因为你在某些内部上下文中声明了 Platform。)

换句话说,Scala 的 Tuple2类型不依赖。我们可以通过创建自己的类来纠正这个问题:
case class DepPair(p: Platform)(a: p.Arch)

但是,Scala 尚不支持依赖类签名,因此无法编译。相反,我们设置使用一个特征:
trait Dep {
val plat: Platform
val arch: plat.Arch
}

Platform.parse("ios")
.flatMap { p => p.parseArch("arm64").map { a =>
new Dep { val plat: p.type = p; val arch: p.Arch = a }}}
.flatMap { dep => exec(dep.plat)(dep.arch) }

请注意 val plat 上的归属和 val arch ,如果没有它们,Scala 将尝试推断出一种会使类型检查失败的精炼类型。

事实上,我们正处于 Scala 合理的边界(恕我直言)。例如,如果我们已经参数化 trait Dep[P <: Platform] ,我们会遇到各种各样的问题。尤其:
Error:(98, 15) type mismatch;
found : Platform => Option[Dep[p.type]] forSome { val p: Platform }
required: Platform => Option[B]

Scala 推断存在性函数类型,但我们实际上想要的是在函数类型内部具有存在性量化。我们必须引导 Scala 理解这一点,我们最终会得到类似的结果:
Platform.parse("ios").flatMap[Dep[p.type] forSome { val p: Platform }]{
case p => p.parseArch("arm64").map{case a: p.Arch =>
new Dep[p.type] { val plat: p.type = p; val arch = a }}}
.flatMap { dep => exec(dep.plat)(dep.arch) }

现在我让你决定哪种方式最好:坚持业主 val周围(简单的解决方案),或者冒着失去任何理智的风险!

但是谈论失去理智和存在主义,让我们尝试进一步调查......

使用existentials(失败)

您代码中中间结果的问题类型是 Option[(Platform, Platform#Arch)] .
实际上有一种方法可以更好地表达它,使用存在主义,例如:
Option[(p.type, p.Arch) forSome {val p: Platform}]

我们可以通过显式指定来帮助 Scala,因此中间结果具有预期的类型:
val tmp: Option[(p.type, p.Arch) forSome {val p: Platform}] =
Platform.parse("ios")
.flatMap { case p => p.parseArch("arm64").map { a => (p, a): (p.type, p.Arch) }}

然而,我们现在触及了 Scala 类型系统的一个非常敏感的领域,它往往会导致问题。其实我没找到表达第二个 flatMap的方法...

正在尝试 tmp.flatMap { case (p, a) => exec(p)(a) }给出了非常有帮助的:
Error:(30, 30) type mismatch;
found : a.type (with underlying type p.Arch)
required: p.Arch

另一个试验:
tmp.flatMap {
(tup: (p.type, p.Arch) forSome {val p: Platform}) => exec(tup._1)(tup._2)
}

Error:(32, 79) type mismatch;
found : tup._2.type (with underlying type p.Arch)
required: tup._1.Arch

在这一点上,我认为任何理性的人都会放弃——并且可能会远离 Scala 编程几天;-)

关于scala - 传递路径依赖类型无法保留依赖值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34653242/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com