我在Scala中有这个版本的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
部分以元组的形式提供元素?
这可以实现吗?
答案 0 :(得分:2)
实际上并不那么难。您需要一种从元组(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
部分并将Loan2
部分替换为使用无形的LoanN
部分,并且适用于每个arity&gt; 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
作为参数并将其转换为元组。这也是可能的,HList
。当然,该API的用户必须自己构造Tuple1
,并且您仍然遇到scala没有用于解开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
相结合,你有一个相对简单的解决方案:
<form method='post' action='j_security_check'>
<input type='text' name='j_username'>
<input type='password' name='j_password'>
</form>