scala resource-management

Patrón de Scala simple para “usar/try-with-resources”(gestión automática de recursos)



resource-management (7)

C # tiene using con la interfaz IDisposable . Java 7+ tiene una funcionalidad idéntica con la interfaz try y AutoCloseable . Scala le permite elegir su propia implementación para este problema.

scala-arm parece ser la opción popular, y es mantenido por uno de los empleados de Typesafe. Sin embargo, parece muy complicado para un comportamiento tan simple. Para aclarar, las instrucciones de uso son simples, pero entender cómo todo ese código funciona internamente es bastante complejo.

Acabo de escribir la siguiente solución ARM súper simple:

object SimpleARM { def apply[T, Q](c: T {def close(): Unit})(f: (T) => Q): Q = { try { f(c) } finally { c.close() } } }

  • ¿Hay algún beneficio para algo como simple-arm? Parece que toda la complejidad adicional debería ofrecer un beneficio adicional.
  • Normalmente, es altamente preferible usar una biblioteca pública, de código abierto, que sea compatible con otros para un comportamiento de propósito general sobre el uso de código personalizado.
  • ¿Alguien puede recomendar alguna mejora?
  • ¿Existen limitaciones para este enfoque simple?

Aquí está mi nuevo sencillo, entiendo de un vistazo, Scala ARM. Esto es totalmente compatible con todos los casos de uso que se me ocurran, incluyendo múltiples recursos y valores de rendimiento. Esto usa una muy simple sintaxis de uso de comprensión:

class AutoCloseableWrapper[A <: AutoCloseable](protected val c: A) { def map[B](f: (A) => B): B = { try { f(c) } finally { c.close() } } def foreach(f: (A) => Unit): Unit = map(f) // Not a proper flatMap. def flatMap[B](f: (A) => B): B = map(f) // Hack :) def withFilter(f: (A) => Boolean) = this } object Arm { def apply[A <: AutoCloseable](c: A) = new AutoCloseableWrapper(c) }

Aquí está el uso de demostración:

class DemoCloseable(val s: String) extends AutoCloseable { var closed = false println(s"DemoCloseable create ${s}") override def close(): Unit = { println(s"DemoCloseable close ${s} previously closed=${closed}") closed = true } } object DemoCloseable { def unapply(dc: DemoCloseable): Option[(String)] = Some(dc.s) } object Demo { def main(args: Array[String]): Unit = { for (v <- Arm(new DemoCloseable("abc"))) { println(s"Using closeable ${v.s}") } for (a <- Arm(new DemoCloseable("a123")); b <- Arm(new DemoCloseable("b123")); c <- Arm(new DemoCloseable("c123"))) { println(s"Using multiple resources for comprehension. a.s=${a.s}. b.s=${b.s}. c.s=${c.s}") } val yieldInt = for (v <- Arm(new DemoCloseable("abc"))) yield 123 println(s"yieldInt = $yieldInt") val yieldString = for (DemoCloseable(s) <- Arm(new DemoCloseable("abc")); c <- s) yield c println(s"yieldString = $yieldString") println("done") } }


Choppy''s Lazy TryClose monad puede ser lo que está buscando. Es muy similar al intento de Scala pero cierra automáticamente los recursos automáticamente.

val ds = new JdbcDataSource() val output = for { conn <- TryClose(ds.getConnection()) ps <- TryClose(conn.prepareStatement("select * from MyTable")) rs <- TryClose.wrap(ps.executeQuery()) } yield wrap(extractResult(rs)) // Note that Nothing will actually be done until ''resolve'' is called output.resolve match { case Success(result) => // Do something case Failure(e) => // Handle Stuff }

Consulte aquí para obtener más información: https://github.com/choppythelumberjack/tryclose


Este es el código que uso:

def use[A <: { def close(): Unit }, B](resource: A)(code: A ⇒ B): B = try code(resource) finally resource.close()

A diferencia de Java try-with-resources, el recurso no necesita implementar AutoCloseable . Sólo se necesita un método close() . Solo soporta un recurso.

Aquí hay un ejemplo de uso con un InputStream :

val path = Paths get "/etc/myfile" use(Files.newInputStream(path)) { inputStream ⇒ val firstByte = inputStream.read() .... }


Su enfoque con un solo patrón de préstamo simple funciona bien siempre y cuando no necesite trabajar con varios recursos, todos necesitan ser administrados. Eso está permitido con el enfoque monádico de scala-arm.

import resource.managed managed(openResA).and(managed(openResB)) acquireFor { (a, b) => ??? } val res = for { a <- managed(openResA) b <- managed(openResB) c <- managed(openResC) } yield (a, b, c) res acquireAndGet { case (a, b, c) => ??? }

Las funciones principales que se deben conocer en scala-arm son resource.managed y .acquired{For,AndGet} , no son realmente complejas por cierto.


Una mejora que puedo recomendar al enfoque que sugirió, que es:

def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): B = { try code(resource) finally resource.close() }

Es utilizar:

def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): Try[B] = { val tryResult = Try {code(resource)} resource.close() tryResult }

En mi humilde opinión, tener el tryResult que es un Try[B] , le permitirá un flujo de control más fácil más adelante.


este me funciona muy bien

implicit class ManagedCloseable[C <: AutoCloseable](resource: C) { def apply[T](block: (C) => T): T = { try { block(resource) } finally { resource.close() } }

usándolo por ejemplo en este código de cliente de Apache Cassandra:

val metadata = Cluster.builder().addContactPoint("vader").withPort(1234).build() { cluster => cluster.getMetadata }

o incluso más corto:

val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)