2010-03-07 2 views
25

я определил «используя» функцию следующим образом:Scala: «с помощью» функции

def using[A, B <: {def close(): Unit}] (closeable: B) (f: B => A): A = 
    try { f(closeable) } finally { closeable.close() } 

я могу использовать его так:

using(new PrintWriter("sample.txt")){ out => 
    out.println("hellow world!") 
} 

теперь я интересно, как определить " с помощью функции»принимать любое количество параметров, и быть в состоянии получить доступ к ним отдельно:

using(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt")){ (in, out) => 
    out.println(in.readLIne) 
} 
+1

Дубликат: http://stackoverflow.com/questions/2207425/what-automatic-resource-management-alternatives-exists- for-scala – retronym

+1

Сделайте 'closeable.close()' внутри 'try', или вы можете маскировать исключения в' f (closeable) '. –

+0

Связанный http://stackoverflow.com/q/7602804/243233 – Jus12

ответ

2

к сожалению, не поддержка произвольной длины в год линейные списки с произвольными типами в стандартной версии Scala.

Возможно, вы сможете сделать что-то подобное с помощью нескольких языковых изменений (чтобы разрешить перенос списков переменных параметров как HLists, см. here примерно на 1/3 того, что необходимо).

Прямо сейчас, самое лучшее, что нужно сделать, это просто сделать то, что выполняет Tuple и Function: реализовать useN для любого количества N, сколько вам нужно.

Два достаточно легко, конечно:

def using2[A, B <: {def close(): Unit}, C <: { def close(): Unit}](closeB: B, closeC: C)(f: (B,C) => A): A = { 
    try { f(closeB,closeC) } finally { closeB.close(); closeC.close() } 
} 

Если вам нужно больше, это, вероятно, стоит написать что-то, что будет генерировать исходный код.

+0

два близких в 'finally' небезопасных. – zinking

6

Я думал об этом, и я подумал, что существует другой способ решить эту проблему. Вот мое взятие на поддержку «любое число» параметров (ограничено тем, что обеспечивают кортежи):

object UsingTest { 

    type Closeable = {def close():Unit } 

    final class CloseAfter[A<:Product](val x: A) { 
    def closeAfter[B](block: A=>B): B = { 
     try { 
     block(x); 
     } finally { 
     for (i <- 0 until x.productArity) { 
      x.productElement(i) match { 
      case c:Closeable => println("closing " + c); c.close() 
      case _ => 
      } 
     } 
     } 
    } 
    } 

    implicit def any2CloseAfter[A<:Product](x: A): CloseAfter[A] = 
    new CloseAfter(x) 

    def main(args:Array[String]): Unit = { 
    import java.io._ 

    (new BufferedReader(new FileReader("in.txt")), 
    new PrintWriter("out.txt"), 
    new PrintWriter("sample.txt")) closeAfter {case (in, out, other) => 
     out.println(in.readLine) 
     other.println("hello world!") 
    } 
    } 
} 

Я думаю, что я повторно использовать тот факт, что классы 22 Кортеж/продукта были написаны в библиотеке ... Я не думаю, что этот синтаксис более ясный, чем с использованием вложенных using (не каламбур), но это была интересная головоломка.

изменить: заменил назначение val case (in, out, other) как предложено retronym.

+1

Thats хороший трюк, жаль, что вы жертвуете безопасностью типа в содержимом кортежа. Закрытие может быть более чистым, если вы замените 'x => val (in, out, other) = x' на' case (in, out, other) => ' – retronym

+0

Спасибо! Мне было интересно, было ли что-то лучше, чем назначение val. Я обновлю ответ. – huynhjl

2

Вот пример, который позволяет использовать scala для понимания как автоматический блок управления ресурсами для любого элемента, который является java.io.Closeable, но его можно легко расширить, чтобы работать для любого объекта с закрытием метод.

Это использование кажется довольно близким к оператору using и позволяет легко иметь столько ресурсов, которые определены в одном блоке, как вы хотите.

object ResourceTest{ 
    import CloseableResource._ 
    import java.io._ 

    def test(){ 
    for(input <- new BufferedReader(new FileReader("/tmp/input.txt")); output <- new FileWriter("/tmp/output.txt")){ 
     output.write(input.readLine) 
    } 
    } 
} 

class CloseableResource[T](resource: =>T,onClose: T=>Unit){ 
    def foreach(f: T=>Unit){ 
    val r = resource 
    try{ 
     f(r) 
    } 
    finally{ 
     try{ 
     onClose(r) 
     } 
     catch{ 
     case e => 
      println("error closing resource") 
      e.printStackTrace 
     } 
    } 
    } 
} 

object CloseableResource{ 
    implicit def javaCloseableToCloseableResource[T <: java.io.Closeable](resource:T):CloseableResource[T] = new CloseableResource[T](resource,{_.close}) 
} 
10

Кто-то уже сделал это — это называется Scala ARM.

Из ридми:

import resource._ 
for(input <- managed(new FileInputStream("test.txt")) { 
    // Code that uses the input as a FileInputStream 
} 
1

Это решение не вполне имеют синтаксис вы хотите, но я думаю, что это достаточно близко :)

def using[A <: {def close(): Unit}, B](resources: List[A])(f: List[A] => B): B = 
    try f(resources) finally resources.foreach(_.close()) 

using(List(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt"))) { 
    case List(in: BufferedReader, out: PrintWriter) => out.println(in.readLine()) 
} 

Конечно вниз сторона вы должны ввести типы BufferedReader и PrintWrter в блок использования.Возможно, вы сможете добавить магию, так что вам нужно только List(in, out), используя multiple ORed type bounds для использования типа A.

Определив некоторые довольно взломанные и опасные неявные преобразования, вы можете столкнуться с необходимостью набирать List (и еще один способ обойти указания типов для ресурсов), но я не документировал детали, поскольку это слишком опасно для ИМО.

0

Рекомендуется отменить алгоритм очистки из пути к программе.

Это решение позволяет накапливать закрытые объекты в объеме.
Очистка области будет происходить после выполнения блока, или область действия может быть отсоединена. Затем очистка области может быть выполнена позже.

Таким образом, мы получаем одно и то же удобство, не ограничиваясь одним программированием нитей.

Класс утилиты:

import java.io.Closeable 

object ManagedScope { 
    val scope=new ThreadLocal[Scope](); 
    def managedScope[T](inner: =>T):T={ 
    val previous=scope.get(); 
    val thisScope=new Scope(); 
    scope.set(thisScope); 
    try{ 
     inner 
    } finally { 
     scope.set(previous); 
     if(!thisScope.detatched) thisScope.close(); 
    } 
    } 

    def closeLater[T <: Closeable](what:T): T = { 
    val theScope=scope.get(); 
    if(!(theScope eq null)){ 
     theScope.closeables=theScope.closeables.:+(what); 
    } 
    what; 
    } 

    def detatchScope(): Scope={ 
    val theScope=scope.get(); 
    if(theScope eq null) null; 
    else { 
     theScope.detatched=true; 
     theScope; 
    } 
    } 
} 

class Scope{ 
    var detatched=false; 
    var closeables:List[Closeable]=List(); 

    def close():Unit={ 
    for(c<-closeables){ 
     try{ 
     if(!(c eq null))c.close(); 
     } catch{ 
     case e:Throwable=>{}; 
     } 
    } 
    } 
} 

Использование:

def checkSocketConnect(host:String, portNumber:Int):Unit = managedScope { 
    // The close later function tags the closeable to be closed later 
    val socket = closeLater(new Socket(host, portNumber)); 
    doWork(socket); 
    } 

    def checkFutureConnect(host:String, portNumber:Int):Unit = managedScope { 
    // The close later function tags the closeable to be closed later 
    val socket = closeLater(new Socket(host, portNumber)); 
    val future:Future[Boolean]=doAsyncWork(socket); 

    // Detatch the scope and use it in the future. 
    val scope=detatchScope(); 
    future.onComplete(v=>scope.close()); 
    } 
Смежные вопросы