從這個關于 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
}
uj5u.com熱心網友回復:
抱歉……我借用了一些 Haskell 式的“型別運算子”來讓演講的幻燈片更適合,但我認為這只會造成更多的混亂。
F : : G將類似于Coproduct[F, G, ?]where 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是這些天要走的路。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/524174.html
