웹에서 Scala 용 ARM (자동 리소스 관리) 예제를 많이 보았습니다. 대부분은 서로 비슷해 보이지만 하나를 쓰는 것은 통과 의례 인 것 같습니다. 나는 한 하지만, 연속 요청을 사용하여 정말 멋진 예제를 참조하십시오.
여하튼, 그 코드의 상당수는 한 유형 또는 다른 유형의 결함을 가지고 있으므로 가장 정확하고 적절한 버전을 투표 할 수있는 Stack Overflow에 대한 참조를 여기에 갖는 것이 좋을 것이라고 생각했습니다.
답변
지금은 스칼라 2.13 마침내 지원했습니다 try with resources
사용하여 사용 🙂 예 :
val lines: Try[Seq[String]] =
Using(new BufferedReader(new FileReader("file.txt"))) { reader =>
Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
}
또는 Using.resource
회피 사용Try
val lines: Seq[String] =
Using.resource(new BufferedReader(new FileReader("file.txt"))) { reader =>
Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
}
Using doc 에서 더 많은 예제를 찾을 수 있습니다 .
자동 리소스 관리를 수행하는 유틸리티입니다. 리소스를 사용하여 작업을 수행하는 데 사용할 수 있으며 그 후에 리소스를 생성 한 순서와 반대로 해제합니다.
답변
Chris Hansen의 블로그 항목 ‘ARM Blocks in Scala : Revisited'(09 년 3 월 26 일)에서 Martin Odersky의 FOSDEM 프레젠테이션 슬라이드 21에 대해 설명합니다. 합니다. 다음 블록은 슬라이드 21에서 바로 가져옵니다 (허가하에).
def using[T <: { def close() }]
(resource: T)
(block: T => Unit)
{
try {
block(resource)
} finally {
if (resource != null) resource.close()
}
}
-견적 끝-
그런 다음 다음과 같이 호출 할 수 있습니다.
using(new BufferedReader(new FileReader("file"))) { r =>
var count = 0
while (r.readLine != null) count += 1
println(count)
}
이 접근 방식의 단점은 무엇입니까? 이 패턴은 자동 리소스 관리가 필요한 곳의 95 %를 해결하는 것 같습니다.
편집 : 코드 스 니펫 추가
Edit2 : 디자인 패턴 확장 -Pythonwith
문 에서 영감을 얻고 주소 지정 :
- 블록 전에 실행할 문
- 관리 자원에 따라 예외 재발생
- 하나의 using 문으로 두 개의 리소스 처리
- 암시 적 변환 및
Managed
클래스를 제공하여 리소스 별 처리
이것은 Scala 2.8입니다.
trait Managed[T] {
def onEnter(): T
def onExit(t:Throwable = null): Unit
def attempt(block: => Unit): Unit = {
try { block } finally {}
}
}
def using[T <: Any](managed: Managed[T])(block: T => Unit) {
val resource = managed.onEnter()
var exception = false
try { block(resource) } catch {
case t:Throwable => exception = true; managed.onExit(t)
} finally {
if (!exception) managed.onExit()
}
}
def using[T <: Any, U <: Any]
(managed1: Managed[T], managed2: Managed[U])
(block: T => U => Unit) {
using[T](managed1) { r =>
using[U](managed2) { s => block(r)(s) }
}
}
class ManagedOS(out:OutputStream) extends Managed[OutputStream] {
def onEnter(): OutputStream = out
def onExit(t:Throwable = null): Unit = {
attempt(out.close())
if (t != null) throw t
}
}
class ManagedIS(in:InputStream) extends Managed[InputStream] {
def onEnter(): InputStream = in
def onExit(t:Throwable = null): Unit = {
attempt(in.close())
if (t != null) throw t
}
}
implicit def os2managed(out:OutputStream): Managed[OutputStream] = {
return new ManagedOS(out)
}
implicit def is2managed(in:InputStream): Managed[InputStream] = {
return new ManagedIS(in)
}
def main(args:Array[String]): Unit = {
using(new FileInputStream("foo.txt"), new FileOutputStream("bar.txt")) {
in => out =>
Iterator continually { in.read() } takeWhile( _ != -1) foreach {
out.write(_)
}
}
}
답변
다니엘,
최근에 자동 리소스 관리를 위해 scala-arm 라이브러리를 배포했습니다. 여기에서 문서를 찾을 수 있습니다 : https://github.com/jsuereth/scala-arm/wiki
이 라이브러리는 세 가지 사용 스타일을 지원합니다 (현재).
1) 명령형 / 표현식 :
import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}
2) 모나 딕 스타일
import resource._
import java.io._
val lines = for { input <- managed(new FileInputStream("test.txt"))
val bufferedReader = new BufferedReader(new InputStreamReader(input))
line <- makeBufferedReaderLineIterator(bufferedReader)
} yield line.trim()
lines foreach println
3) 구분 된 연속 스타일
다음은 “echo”tcp 서버입니다.
import java.io._
import util.continuations._
import resource._
def each_line_from(r : BufferedReader) : String @suspendable =
shift { k =>
var line = r.readLine
while(line != null) {
k(line)
line = r.readLine
}
}
reset {
val server = managed(new ServerSocket(8007)) !
while(true) {
// This reset is not needed, however the below denotes a "flow" of execution that can be deferred.
// One can envision an asynchronous execuction model that would support the exact same semantics as below.
reset {
val connection = managed(server.accept) !
val output = managed(connection.getOutputStream) !
val input = managed(connection.getInputStream) !
val writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output)))
val reader = new BufferedReader(new InputStreamReader(input))
writer.println(each_line_from(reader))
writer.flush()
}
}
}
이 코드는 리소스 유형 특성을 사용하므로 대부분의 리소스 유형에 적응할 수 있습니다. close 또는 dispose 메서드를 사용하여 클래스에 대해 구조적 유형을 사용하는 대체 방법이 있습니다. 추가 할 편리한 기능이 있으면 설명서를 확인하고 알려주십시오.
답변
연속을 사용하는 James Iry 솔루션은 다음과 같습니다 .
// standard using block definition
def using[X <: {def close()}, A](resource : X)(f : X => A) = {
try {
f(resource)
} finally {
resource.close()
}
}
// A DC version of 'using'
def resource[X <: {def close()}, B](res : X) = shift(using[X, B](res))
// some sugar for reset
def withResources[A, C](x : => A @cps[A, C]) = reset{x}
비교를 위해 계속되는 솔루션과 그렇지 않은 솔루션은 다음과 같습니다.
def copyFileCPS = using(new BufferedReader(new FileReader("test.txt"))) {
reader => {
using(new BufferedWriter(new FileWriter("test_copy.txt"))) {
writer => {
var line = reader.readLine
var count = 0
while (line != null) {
count += 1
writer.write(line)
writer.newLine
line = reader.readLine
}
count
}
}
}
}
def copyFileDC = withResources {
val reader = resource[BufferedReader,Int](new BufferedReader(new FileReader("test.txt")))
val writer = resource[BufferedWriter,Int](new BufferedWriter(new FileWriter("test_copy.txt")))
var line = reader.readLine
var count = 0
while(line != null) {
count += 1
writer write line
writer.newLine
line = reader.readLine
}
count
}
그리고 여기에 Tiark Rompf의 개선 제안이 있습니다.
trait ContextType[B]
def forceContextType[B]: ContextType[B] = null
// A DC version of 'using'
def resource[X <: {def close()}, B: ContextType](res : X): X @cps[B,B] = shift(using[X, B](res))
// some sugar for reset
def withResources[A](x : => A @cps[A, A]) = reset{x}
// and now use our new lib
def copyFileDC = withResources {
implicit val _ = forceContextType[Int]
val reader = resource(new BufferedReader(new FileReader("test.txt")))
val writer = resource(new BufferedWriter(new FileWriter("test_copy.txt")))
var line = reader.readLine
var count = 0
while(line != null) {
count += 1
writer write line
writer.newLine
line = reader.readLine
}
count
}
답변
Scala에서 ARM을 수행하기위한 점진적인 4 단계 진화를 봅니다.
- 팔 없음 : 먼지
- 클로저 만 : 더 좋지만 다중 중첩 블록
- Continuation Monad : For를 사용하여 중첩을 평평하게하지만 2 블록으로 부 자연스럽게 분리됩니다.
- 직접적인 스타일 연속 : Nirava, 아하! 이것은 또한 가장 유형이 안전한 대안입니다. withResource 블록 외부의 리소스는 유형 오류입니다.
답변
더 나은 파일에 포함 된 경량 (10 줄의 코드) ARM이 있습니다. 참조 : https://github.com/pathikrit/better-files#lightweight-arm
import better.files._
for {
in <- inputStream.autoClosed
out <- outputStream.autoClosed
} in.pipeTo(out)
// The input and output streams are auto-closed once out of scope
전체 라이브러리를 원하지 않는 경우 구현되는 방법은 다음과 같습니다.
type Closeable = {
def close(): Unit
}
type ManagedResource[A <: Closeable] = Traversable[A]
implicit class CloseableOps[A <: Closeable](resource: A) {
def autoClosed: ManagedResource[A] = new Traversable[A] {
override def foreach[U](f: A => U) = try {
f(resource)
} finally {
resource.close()
}
}
}
답변
Type 클래스 사용 방법
trait GenericDisposable[-T] {
def dispose(v:T):Unit
}
...
def using[T,U](r:T)(block:T => U)(implicit disp:GenericDisposable[T]):U = try {
block(r)
} finally {
Option(r).foreach { r => disp.dispose(r) }
}