実際はそれほど難しくありません。タプル(Generic.Aux[Tup, L]
)からHList
を取得し、Hlist
(ToList[L, AutoCloseable]
)からList[AutoClosable]
を取得する方法が必要です。
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リソースの場合のために、あなたがcase Tuple1(f)
ようTuple1(new Foo)
とパターンマッチを記述する必要があるということです。最も簡単な解決策は、Loan1
の部分を維持して、Loan2
の部分をシェイプレスで実装されたLoanN
に置き換え、すべてのアリティ> 1で動作させることです。だから、あなたの中に私の解決策を貼り付けてLoanN
に私のLoan
クラスの名前を変更コピーすることはほぼ同等です:
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[AutoClosable]
のサブタイプであるList[Nothing]
に変換することができます。
間違いなく、Loan1
の追加の定型文は、1つの引数と1つの引数のタプルを区別できるより複雑な型クラスを自分自身で書くことによって、無くすことができます。
あなたは、引数としてHList
を受け入れ、タプルにそれを変換するために提案しました。これも可能です(shapeless.ops.hlist.Tupler
)。もちろん、そのAPIのユーザーはHList
を構築しなければなりません。そして、あなたはまだTuple1
をアンラップするためのきれいな構文を持たないスケーラの問題を抱えています。その第二の問題は、A
からTuple1[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
を私は上の境界の代わりにtypeclassから始めるでしょう - それはあなたに実装の面でより多くの自由を与える – jdevelop