Primitive Try/match handling in scala - scala

I wrote the following piece of code
def info(): MyCaseClass = {
Try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
} match {
case Failure(f) => {
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
}
case Success(s) => s
}
}
Is there an even shorter way to deal with the fact that the file system operation could result in an exception so I handle it as above. Like can I not just somehow have to deal with failure case only. Like in case of Future exceptions, the future just does what it has to but for exceptions only we define recover and recoverWith. Something analogous is possible here?

Simply use try instead of Try:
def info(): MyCaseClass = {
try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
} catch {
case f: Throwable => {
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
}
}
}

Try has recover as well:
def info(): MyCaseClass = {
(Try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
} recover {
case f =>
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
}).get
}

.getOrElse seems to be what you are looking for:
def info(): MyCaseClass = {
Try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
}.getOrElse({
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
})
}

Related

Play framework ignore following map/flatmap

Is there a way to ignore the following map/flatmap's without failed?
This is what I have:
def delete(serverId: UUID) = authAction.async { implicit request =>
val user = request.user.get
serverService.findByIdAndUserId(serverId, user.id.get)
.flatMap{s =>
if (s.isEmpty) {
Future.failed(new DeleteFailedException)
// Can I return a `NotFound("...")` here instead of failed?
} else {
Future.successful(s.get)
}
}
.map{s =>
serverService.delete(s)
}.map{_ =>
Ok(Json.toJson(Map("success" -> "true")))
}
}
When I would return a NotFound("...") in the flatMap the following map would still be executed. Is there a way to ignore the following map/flatmap's?
Think so should be fine (I assumed that findByIdAndUserId returns Future[Option[_]], not an Option[_] as you answered in comment). In my approach I also removed usage of get and unnecessary map
def delete(serverId: UUID) = authAction.async { implicit request =>
val user = request.user.get
request.user.flatMap(_.id).fold {
Future.successfull(NotFound("no user"))
} {userId =>
serverService.findByIdAndUserId(serverId, userId).map {
case None =>
NotFound("no server")
case Some(s) =>
serverService.delete(s)
Ok(Json.toJson(Map("success" -> "true")))
}
}
}
Instead of doing a Future.failed with an exception. you can return an Either. The good thing about either is that you can pattern match on it and then construct the appropriate http response.
def delete(serverId: UUID) = authAction.async { implicit request =>
val user = request.user.get
serverService.findByIdAndUserId(serverId, user.id.get)
.flatMap{s =>
if (s.isEmpty) {
Left(new DeleteFailedException)
} else {
Right(s.get)
}
}
.map{s => s match {
case Left(notFound) =>
// construct the Json for failure
case Right(s) =>
serverService.delete(s)
// construct json for success.
}}
}

closing file pointer in Scala in Finally

In the following code, I am reading no. of lines from a file. If something goes wrong, I'll like to close the file pointer. But how can I find out if f contains valid pointer or not?
def countLines(filename:String):Option[Int] = {
try{
val f = Source.fromFile(filename)
println(s"no. of lines ${f.getLines().size}")
Some(f.getLines.size)
} catch {
case ex: FileNotFoundException => {
println(s"file ${filename} not found")
None
}
} finally {
//f might not be a valid pointer depending on when the error occured
}
}
The book I am reading uses var to maintain state (if f is valid or not) but I am trying to avoid it for sake of using only immutable variables.
def countLines(filename:String):Option[Int] = {
var f:Option[Source] = None
try{
f = Some(Source.fromFile(filename))
println(s"no. of lines ${f.get.getLines().size}")
Some(f.get.getLines.size)
} catch {
case ex: FileNotFoundException => {
println(s"file ${filename} not found")
None
}
} finally {
for(i<-f){
println("closing file")
i.close()
}
}
}
A double Try(). This closes the io resource even if the getLines() fails, but only if the fromFile() succeeds.
import scala.util.Try
def countLines(filename: String): Option[Int] =
Try(io.Source.fromFile(filename)).fold(_ => None, {f =>
val count = Try(f.getLines().length)
f.close()
count.toOption
})
What do you think about this?
If you want Scala-way - i think it's good example for your task:
def countLines(filename: String): Try[Int] = Try(Source.fromFile(filename).getLines.toList.size)
def test() = {
val filename = "/etc/passwd"
countLines(filename) match {
case Success(n) => println(n)
case Failure(f) => println(f)
}
}
When n - is a number of our lines, and f - is a Throwable.
How about this:
def countLines(filename: String): Option[Int] = {
val file = Try(Source.fromFile(filename))
val count = file.map(_.getLines().size)
(for {
_ <- count.recoverWith { case _ => file.map(_.close()) }
lineCount <- count
} yield lineCount).toOption
}
Let's analyze it:
If file does not exist we will have failed Try instance and method returns None. In this case you do not need to clear any resources as no actual stream was created.
If getLines fails for any reason or anything else during processing goes south we will close created stream in first line of for comprehension
Hope it helps
Simply, how about this:
def numLines(fileName:String):Option[Int] = {
try {
val f = scala.io.Source.fromFile(fileName)
try { Some(f.getLines.size) }
catch { case ex: IOException =>
Console.err.println("i/o excetion")
None
}
finally { f.close() }
}
catch {
case ex: FileNotFoundException =>
Console.err.println("file not found")
None
}
}

In scala, is there an idiomatic way to handle closing and reopening an underlying stream when trying to wrap it with other streams?

I am trying to open a stream, try to decompress it as gzip, if that fails try to decompress it as zlib, and return the stream for further use. The underlying stream must be closed in the case of exceptions creating the wrapping decompression streams, as otherwise I will run into resource exhaustion issues.
pbData is a standard, non-resettable, InputStream
There has to be a cleaner way to do this.
val input = {
var pb = pbData.open()
try {
log.trace("Attempting to create GZIPInputStream")
new GZIPInputStream(pb)
} catch {
case e: ZipException => {
log.trace("Attempting to create InflaterInputStream")
pb.close()
pb = pbData.open()
try {
new InflaterInputStream(pb)
} catch {
case e: ZipException => {
pb.close()
throw e
}
}
}
}
Your process is actually iteration over InputStream instance generators. Check this, far more idiomatic solution compared to a lot of nested try-catch'es:
val bis = new BufferedInputStream(pbData.open())
// allows us to read and reset 16 bytes, as in your answer
bis.mark(16)
// list of functions, because we need lazy evaluation of streams
val streamGens: List[Any => InputStream] = List(
_ => new GZIPInputStream(bis),
_ => new InflaterInputStream(bis),
_ => bis
)
def firstStreamOf(streamGens: List[Any => InputStream]): Try[InputStream] =
streamGens match {
case x :: xs =>
Try(x()).recoverWith {
case NonFatal(_) =>
// reset in case of failure
bis.reset()
firstStreamOf(xs)
}
case Nil =>
// shouldn't get to this line because of last streamGens element
Failure(new Exception)
}
val tryStream = firstStreamOf(streamGens)
tryStream.foreach { stream =>
// do something with stream...
}
As a bonus, if you'll need to add trying more stream generators, you will have to add exactly one line into streamGens initialization. Also, we won't need to add bit.reset() invocation manually.
Something like this, perhaps:
def tryDecompress(pbData: PBData, ds: InputStream => InputStream *): InputStream = {
def tryIt(s: InputStream, dc: InputStream => InputStream) = try {
dc(s)
} catch { case NonFatal(e) =>
close(s)
throw e
}
val (first, rest) = ds.head -> ds.tail
try {
tryIt(pbData.open, first)
} catch {
case _: ZipException if rest.nonEmpty =>
tryDecompress(pbData, rest)
}
}
val stream = tryDecompress(pbData, new GZipInputStream(_), new InflaterInputStream(_))
(Too bad scala's Try does not have onFailure ... this could look much nicer if it did :/)
val result: Try[InflaterInputStream] = Try(new GZIPInputStream(pb)) match {
case res#Success(x) => res
case Failure(e) => e match {
case e: ZipException =>
Try(new InflaterInputStream(pb)) match {
case res2#Success(x2) => res2
case Failure(e2) => e match {
case _ => pb.close()
Failure(e2)
}
}
case ex#_ => pb.close()
Failure(ex)
}
}
I found that using a BufferedInputStream to wrap the underlying stream, then resetting it between each decompression library attempt, looks pretty clean.
val bis = new BufferedInputStream(pbData.open())
// allows us to read and reset 16 bytes
bis.mark(16)
val input: InputStream = {
try {
log.trace("attempting to open as gzip")
new GZIPInputStream(bis)
} catch {
case e: ZipException => try {
bis.reset()
log.trace("attempting to open as zlib")
new InflaterInputStream(bis)
} catch {
case e: ZipException => {
bis.reset()
log.trace("attempting to open as uncompressed")
bis
}
}
}
}

How to generalize a method?

I'm trying to generalize the following code:
def fetchUrl = {
try {
val lineList = Source.fromURL(url).getLines.toList
process(lineList)
}
catch {
case ex: java.net.UnknownHostException => ex.printStackTrace()
}
}
I want to be able to fetch URL's (fromURL) and files (fromFile) with the same method. Is it possible to generalize this code to archive this?
I figured I could use pattern matching for this but I don't know how.
def fetchSource(src: Source, str: String) = src match {
case ??? => Source.fromURL(url).getLines.toList
case ??? => Source.fromFile(str).getLines.toList
}
Is there a way to get this right?
Thanks in advance.
You could check if a string is a Url (for this example I'm using java.net.URL, but you can do it with UrlValidator as well)
Something like this:
def isUrl(url: String) = try {
new java.net.URL(url)
true
} catch {
case _ => false
}
import scala.io.Source
def fetchSource(src:String) = if(isUrl(src)) {
Source.fromURL(src).getLines.toList
} else {
Source.fromFile(src).getLines.toList
}
The simplest solution is to have one method that will fetch a given Source, and two wrapper methods that build Source from File or URL.
def fetchSource(source: io.Source) =
try {
val lineList = source.getLines.toList
process(lineList)
} catch {
case ex: java.net.UnknownHostException => ex.printStackTrace()
}
def fetchFile(file: java.io.File) = fetchSource(io.Source.fromFile(file))
def fetchUrl(url: java.net.URL) = fetchSource(io.Source.fromURL(url))

How do I ignore an exception?

Is there more elegant way to write the following?
try {
... // Some throwing code
return first
}
catch {
case e:ExceptionType => {} // No code to execute. Ignore error.
}
return second
scala.util.control.Exception.ignoring(classOf[ExceptionType]) {
... // Some throwing code
}
#Daniel has already provided the canonical method to use to do this. Look through the other methods in scala.util.control.Exception--they are quite helpful and generic!
If you need to get a return value out of the try block, use failing instead of ignoring (but be aware that the result is an Any, i.e. not typesafe).
You can also write your own exception-catcher, which will be a little slow for heavy-duty work but otherwise nice to use:
class DefaultOn[E <: Exception] {
def apply[A](default: => A)(f: => A)(implicit m: Manifest[E]) = {
try { f } catch { case x if (m.erasure.isInstance(x)) => default }
}
}
object DefaultOn { def apply[E <: Exception] = new DefaultOn[E] }
scala> DefaultOn[NumberFormatException](0) { "Hi".toInt }
res0: Int = 0
Or if you like options:
class TryOption[E <: Exception] {
def apply[A](f: => A)(implicit m: Manifest[E]) = {
try { Some(f) } catch { case x if (m.erasure.isInstance(x)) => None }
}
}
object TryOption { def apply[E <: Exception] = new TryOption[E] }
scala> TryOption[NumberFormatException] { "Hi".toInt }
res1: Option[Int] = None
Or you can be inspired by this plus the library routines and create your own methods to ignore multiple different exceptions and preserve types on the return value.
In Scala all exceptions are not checked, so if you don't want, you may just skip handling them (and thus exception will be escalated to a higher level). Silently ignoring an exception the way you want to do is generally a bad practice. However, your code can be shortened to:
try {
... // Some throwing code
} catch {
case e:ExceptionType =>
}
Hows about:
Try {
// some throwing code
}
This will ignore all non fatal exceptions, which is what you want to do most of the time.