在这个Scala示例中,泛型类型约束“:< :”和“:+:”是什么意思?

xvw2m8pv  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(581)

从这篇关于2017年Nanopass编译器(https://github.com/sellout/recursion-scheme-talk/blob/master/nanopass-compiler-talk.org)的演讲中,我发现了下面的代码片段。在这段代码中,我看到了两个通用约束,我到处搜索以了解它们,但未能找到有关它们的任何信息。我希望了解的是:

**这些运营商在做什么?

  • 他们从哪里来?
  • 在最新版本的Scala和相关库中是否有更现代的等价物?
final case class Let[A](bindings: List[(String, A)], body: A)
final case class If[A](test: A, consequent: A, alt: A)

def expandLet[Lambda :<: F]: Fix[Let :+: F] => Fix[F] =
  _.unFix match {
    case Let(bindings, body) =>
      bindings.unzip((names, exprs) =>
        Fix(App(Fix(Lam(names, expandLet(body)).inject),
                exprs.map(expandLet)).inject))
    // and don’t forget the other cases
  }

def expandIf[Lambda :<: F]: Fix[If :+: F] => Fix[F] =
  _.unFix match {
    case If(test, consequent, alt) =>
      Fix(App(expandIf(test), List(
        Fix(Lam(Nil, expandIf(consequent))),
        Fix(Lam(Nil, expandIf(alt))))))
    // seriously, still gotta handle the other cases
  }
jdg4fx2g

jdg4fx2g1#

对不起,…我借用了一些Haskell-y类型的运算符,以使幻灯片上的内容更适合演讲,但我认为这只是造成了更多的困惑。
F :+: G类似于Coproduct[F, G, ?],其中type Coproduct[F, G, A] = Either[F[A], G[A]]。也就是说,它允许你组成模式函数器,用更小的片段构建更丰富的语言。
F :<: G稍微复杂一些。它将类似于Contains[F, G],其中

trait Contains[F, G] {
  def inject[A](in: F[A]): G[A]
  def project[A](outOf: G[A]): Option[F[A]]
}

val theSame[F] = new Contains[F, F] {
    def inject[A](in: F[A]) = in
    def project[A](outOf: F[A]) = Some(outOf)
}

val onTheLeft[F, G] = new Contains[F, Coproduct[F, G]] {
    def inject[A](in: F[A]) = Left(in)
    def project[A](outOf: Coproduct[F, G]) = outOf match {
        case Left(in) => Some(in)
        case Right(_) => None
    }
}

val nested[F, G, H](implicit further: Contains[F, H]) =
    new Contains[F, Coproduct[G, H]] {
        def inject[A](in: F[A]) = Right(further.inject(in))
        def project[A](outOf: Coproduct[G, H]) = outOf match {
            case Left(_) => None
            case Right(h) => further.project(h)
        }
    }

所以这段代码的一个更好的版本(尽管仍然无效--我已经几年没有编写Scala了)是

def expandLet[F](input: Fix[Coproduct[Let, F]])
                (implicit contains: Contains[Lambda, F])
    : Fix[F] =
  input.unFix match {
    case Left(Let(bindings, body)) =>
      bindings.unzip((names, exprs) =>
        Fix(App(Fix(Lam(names, expandLet(body)).inject),
                exprs.map(expandLet)).inject))
    // and don’t forget the other cases
  }

我仍然在使用这项技术,只是不是在Scala中,而且我使用它的方式有了一些变化(例如,我会将expandLet变成一个代数,以消除递归调用)。但至少,那次演讲中的概念仍然是相关的。
如果您想用Scala编写这样的代码,我认为Droste是目前的发展方向。

相关问题