gpt4 book ai didi

scala - 将无形 HList 转换为元组

转载 作者:行者123 更新时间:2023-12-04 19:59:05 25 4
gpt4 key购买 nike

我有这个版本的 try-with-resources在斯卡拉。我想知道是否可以使用 Shapeless 和 HList 制作一个通用版本?

import scala.util.{Failure, Success, Try}

class Loan1[A <: AutoCloseable](resource: A) {
def to[B](block: A => B): B = {
Try(block(resource)) match {
case Success(result) =>
resource.close()
result
case Failure(e) =>
resource.close()
throw e
}
}
}

class Loan2[A <: AutoCloseable, B <: AutoCloseable](r1: A, r2: B){
def to[R](block: (A,B) => R): R = {
Try(block(r1,r2)) match {
case Success(result) =>
r1.close(); r2.close()
result
case Failure(e) =>
r1.close(); r2.close()
throw e
}
}
}

object Loan {

def apply[A <: AutoCloseable](resource: A): Loan1[A] = new Loan1(resource)

def apply[A <: AutoCloseable, B <: AutoCloseable] (r1: A, r2: B)= new Loan2(r1, r2)

}

有类似签名的东西,我猜
  def apply[L <: HList](list: L)(implicit con: LUBConstraint[L, AutoCloseable]) = ???

还有一个问题是如何在 block: (A,B) => R 中以元组的形式提供元素。部分?

这有可能实现吗?

最佳答案

其实没那么难。您需要一种方法来获得 HList从元组( Generic.Aux[Tup, L] )和获得 List[AutoClosable] 的方法来自 Hlist (ToList[L, AutoCloseable])。

除了 ToList 之外,可能还有其他方法可以做到这一点。部分,但它是 LUBConstraint[L, AutoCloseable] 的简单融合以及能够调用close()的要求在每个资源上。

scala> :paste
// Entering paste mode (ctrl-D to finish)

import shapeless._, ops.hlist._
import scala.util.{Failure, Success, Try}

class Loan[Tup, L <: HList](resources: Tup)(
implicit
gen: Generic.Aux[Tup, L],
con: ToList[L, AutoCloseable]
) {
def to[B](block: Tup => B): B = {
Try(block(resources)) match {
case Success(result) =>
gen.to(resources).toList.foreach { _.close() }
result
case Failure(e) =>
gen.to(resources).toList.foreach { _.close() }
throw e
}
}
}

object Loan {
def apply[Tup, L <: HList](resources: Tup)(
implicit
gen: Generic.Aux[Tup, L],
con: ToList[L, AutoCloseable]
) = new Loan(resources)
}

// Exiting paste mode, now interpreting.


scala> class Bar() extends AutoCloseable { def close = println("close Bar"); def IAmBar = println("doing bar stuff") }
defined class Bar

scala> class Foo() extends AutoCloseable { def close = println("close Foo"); def IAmFoo = println("doing foo stuff") }
defined class Foo

scala> Loan(new Foo, new Bar).to{ case (f, b) => f.IAmFoo; b.IAmBar }
doing foo stuff
doing bar stuff
close Foo
close Bar

唯一的问题是,对于恰好 1 个资源的情况,您需要编写 Tuple1(new Foo)和模式匹配,如 case Tuple1(f) .最简单的解决方案是保留 Loan1零件并更换 Loan2LoanN 分开这是用无形实现的,适用于每一个 >1 的数量。所以这几乎等于将我的解决方案复制粘贴到您的解决方案中并重命名我的 Loan上课至 LoanN :
import shapeless._, ops.hlist._, ops.nat._
import scala.util.{Failure, Success, Try}

class LoanN[Tup, L <: HList](resources: Tup)(
implicit
gen: Generic.Aux[Tup, L],
con: ToList[L, AutoCloseable]
) {
def to[B](block: Tup => B): B = {
Try(block(resources)) match {
case Success(result) =>
gen.to(resources).toList.foreach { _.close() }
result
case Failure(e) =>
gen.to(resources).toList.foreach { _.close() }
throw e
}
}
}

class Loan1[A <: AutoCloseable](resource: A) {
def to[B](block: A => B): B = {
Try(block(resource)) match {
case Success(result) =>
resource.close()
result
case Failure(e) =>
resource.close()
throw e
}
}
}


object Loan {
def apply[A <: AutoCloseable](resource: A): Loan1[A] = new Loan1(resource)
def apply[Tup, L <: HList, Len <: Nat](resources: Tup)(
implicit
gen: Generic.Aux[Tup, L],
con: ToList[L, AutoCloseable],
length: Length.Aux[L, Len],
gt: GT[Len, nat._1]
) = new LoanN(resources)
}

我还添加了输入的长度必须大于 1 的约束。否则会有一个漏洞,你传入 case class Baz()。可以转换为 List[Nothing]它是 List[AutoClosable] 的子类型.

毫无疑问, Loan1 是额外的样板文件。仍然可以通过自己编写更复杂的类型类来消除东西,该类型类能够区分单个参数和参数元组。

您提议接受 HList作为参数并将其转换为元组。这也是可能的,使用 shapeless.ops.hlist.Tupler .然后,该 API 的用户当然必须构造 HList他们自己,你仍然有 scala 没有漂亮的语法来解开 Tuple1 的问题。 .第二个问题可以通过一个非常简单的自定义类型类来解决,该类型类展开 Tuple1[A]。到 A并保持其他所有内容不变:
sealed trait Unwrap[In] { 
type Out
def apply(in: In): Out
}

object Unwrap extends DefaultUnwrap {
type Aux[In, Out0] = Unwrap[In] { type Out = Out0 }
def apply[T](implicit unwrap: Unwrap[T]): Unwrap.Aux[T, unwrap.Out] = unwrap

implicit def unwrapTuple1[A]: Unwrap.Aux[Tuple1[A], A] = new Unwrap[Tuple1[A]] {
type Out = A
def apply(in: Tuple1[A]) = in._1
}
}
trait DefaultUnwrap {
implicit def dontUnwrapOthers[A]: Unwrap.Aux[A, A] = new Unwrap[A] {
type Out = A
def apply(in: A) = in
}
}

结合 Tupler你有一个相对简单的解决方案:
scala> :paste
// Entering paste mode (ctrl-D to finish)

import shapeless._, ops.hlist._
import scala.util.{Failure, Success, Try}

class LoanN[Tup, L <: HList, Res](resources: L)(
implicit
tupler: Tupler.Aux[L, Tup],
con: ToList[L, AutoCloseable],
unwrap: Unwrap.Aux[Tup, Res]
) {
def to[B](block: Res => B): B = {
Try(block(unwrap(tupler(resources)))) match {
case Success(result) =>
resources.toList.foreach { _.close() }
result
case Failure(e) =>
resources.toList.foreach { _.close() }
throw e
}
}
}


object Loan {
def apply[Tup, L <: HList, Res](resources: L)(
implicit
tupler: Tupler.Aux[L, Tup],
con: ToList[L, AutoCloseable],
unwrap: Unwrap.Aux[Tup, Res]
) = new LoanN(resources)
}

// Exiting paste mode, now interpreting.


scala> Loan(new Foo :: new Bar :: HNil).to{ case (f,b) => f.IAmFoo; b.IAmBar }
doing foo stuff
doing bar stuff
close Foo
close Bar

scala> Loan(new Foo :: HNil).to{ case (f) => f.IAmFoo }
doing foo stuff
close Foo

关于scala - 将无形 HList 转换为元组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42224808/

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