2017-08-07 84 views
0

是否有只包装传递函数的返回类型的方法?如何在Scala中包装传递函数的返回类型

我试图自动使用自动管理资源try-with-resources scala equivalent。我想要做的是嵌套托管资源,即做类似的事情:

cleanly(db.connect())(_.close()) { connection => 
      cleanly(connection.prepareCall("{call procedure}"))(_.close()) { statement => 
       statement.setInt("batch", 1) 
       statement.execute() 
      } 
     } 

但是,返回Try [Try [B]]。所以我稍微修改了签名和实施清洁功能:

def cleanly[A, B](resource: A)(cleanup: A => Unit)(doWork: A => Try[B]): Try[B] = { 
     try { 
      doWork(resource) 
     } 
     finally { 
      try { 
       if (resource != null) { 
        cleanup(resource) 
       } 
      } catch { 
       case e: Exception => log.error("Error cleaning up resource.", e) // should be logged 
      } 
     } 
    } 

我试着从一个新添加的方法tryCleanly这将包裹传递函数返回一个尝试对象调用它:

def tryCleanly[A, B](resource: A)(cleanup: A => Unit)(doWork: A => B): Try[B] = { 
    cleanly[A,B](resource)(cleanup)(Try(doWork)) 
} 

但是做尝试(的doWork)的结果

Try[(A) => B] 

而不是

(A) => Try[B] 

是否可以只包装函数的结果类型?

回答

1
def tryCleanly[A, B](resource: A)(cleanup: A => Unit)(doWork: A => B): Try[B] = { 
    cleanly[A,B](resource)(cleanup)(a: A => Try(doWork(a))) 
} 
0

您可以将flattenTry[Try[B]]设置为Try[B]。例如:

val try1 = Try(Try(3)) // Try[Try[Int]] 
val try2 = try1.flatten // Try[Int] 

解开任意深度的嵌套Try,您可以采用如下方案(我借这个解决方案从这个answer,功劳都到@SarveshKumarSingh):

import scala.language.higherKinds  

case class Flattener[W[_], WW, T](fn: WW => W[T]) 

implicit def tryRecursiveFlattenFn[WW, T](
    implicit f: Flattener[Try, WW, T] = Flattener((ww: WW) => Try(ww)) 
) = Flattener((ww: Try[WW]) => ww.flatMap(f.fn)) 

def tryRecursiveFlatten[WW, T](www: Try[WW])(
    implicit f: Flattener[Try, Try[WW], T] 
) = f.fn(www) 

val try1 = Try(Try(Try(Try(3)))) // Try[Try[Try[Try[Int]]]] 
val try2 = tryRecursiveFlatten(try1) // Try[Int] 
+0

没错,但我想的是更多的一元风格。如果我拥有两个以上的嵌套资源,那么我将需要多次调用flatten,这并不理想 – user3681304

0

@JoeK激励我再次混淆参数,所以学分归他所有。我最终得到的是这样的:

def cleanly[A, B](resource: A)(cleanup: A => Unit)(doWork: A => Try[B]): Try[B] = { 
    try { 
     doWork(resource) 
    } 
    finally { 
     try { 
      if (resource != null) { 
       cleanup(resource) 
      } 
     } catch { 
      case e: Exception => log.error("Error cleaning up resource.", e) // should be logged 
     } 
    } 
} 

def tryCleanly[A, B](resource: A)(cleanup: A => Unit)(doWork: A => B): Try[B] = { 
    cleanly[A,B](resource)(cleanup)(resource => Try(doWork(resource))) 
}