I am trying to figure out the scala way to implment something I would do all the time in java. In java I would have snarf_image (below) return null if it meets the if condition, otherwise return the bArray. What is the scala way to do this? This code doesnt even compile and I cant figure out the right way to do it - Im sure my thinking is off.
def snarf_image ( sUrl : String ) : Array[Byte] = {
val bis = new BufferedInputStream(new URL(sUrl.replace(" ", "%20")).openStream())
val bArray = Stream.continually(bis.read).takeWhile(-1 !=).map(_.toByte).toArray
val img = ImageProcessing.ArrayToImage(bArray)
if ( img.getHeight < 100 || img.getWidth < 100 ) {
Empty
} else {
bArray
}
}
For the record I am using lift (hence using empty) but Im pretty sure this is more a scala question.
You can sometimes use Option for cases where you want to return null (in Java).
I haven't compiled it, but it should work.
def snarf_image ( sUrl : String ) : Option[Array[Byte]] = {
val bis = new BufferedInputStream(new URL(sUrl.replace(" ", "%20")).openStream())
val bArray = Stream.continually(bis.read).takeWhile(-1 !=).map(_.toByte).toArray
val img = ImageProcessing.ArrayToImage(bArray)
if ( img.getHeight < 100 || img.getWidth < 100 ) {
None
} else {
Some(bArray)
}
}
TLDR: there are many options to use: Option, Box, Either, Try and even Future (and more can be found in different libraries), likely you will be good with Option.
First of all you can use empty collections instead of null -- it is common advice not only for Scala, but for many other languages including Java and C#:
def snarf_image ( sUrl : String ) : Array[Byte] = {
// ...
if ( img.getHeight < 100 || img.getWidth < 100 ) {
Array.empty[Byte]
} else {
bArray
}
}
Of course this might not save you from the surprises, as you may accidentally forgot to check collection for emptiness, but it is more likely that you will got a huge surprise with null.
See also Is it better to return null or empty collection?
Next, comes Option.
Use option when you either have something (Some) or nothing (None), and you don't care why (there is only one reason or it does not matter).
The option is not a scala invention (I've seen it ML languages, in Haskell it is known as Maybe, it even comes to the std lib in java 8 and available to use in earlier java versions as a guava part).
It is used extensively in standard library and you will likely see it in many of the 3rd-party libraries. Canonical example could be retrieval from Map, when there is no such key -- Option emphasizes the fact that Map could not contain such key so you have to either -- dealt with possibility of missing key or propagate it further.
val xys = Map(1 -> "foo", 2 -> "bar")
xys.get(3)
// Option[String] = None
xys.get(1)
// Option[String] = Some(foo)
xys.get(2).toUpperCase
// cannot operate directly on Option -- has to unwrap it first:
// error: value toUpperCase is not a member of Option[String]
Use Either when you want to know why it failed or when you have two possibilities (not only success/failure) or when you use older version of scala (prior to 2.10)
It has quite same roots as an Option and is used not only in Scala but in some other languages (e.g. already mentioned Haskell). Besides doubtful distinction -- Either is not a monad in Scala, the main difference is that you don't have empty element, you have either right or left. It is a way less commonly used than Option because it is not instantly clear what should be right and what should be left, but the common convention is to use Right for the successful value and Left for a faulty path (usually it contains Throwable object with explanation of what gone wrong). The Either is mostly oysted by Try which is not that fabulos and originally born in Twitter (one of the biggest and earliest Scala adopters out there).
Use try for successful value/failure reason in scala 2.10+ (or 2.9.3, since it was back ported)
It is a little bit more convenient since the cases named Success and Failure.
The example from official doc (which is nice by the way):
import scala.util.{Try, Success, Failure}
def divide: Try[Int] = {
val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt)
val divisor = Try(Console.readLine("Enter an Int that you'd like to divide by:\n").toInt)
val problem = dividend.flatMap(x => divisor.map(y => x/y))
problem match {
case Success(v) =>
println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v)
Success(v)
case Failure(e) =>
println("You must've divided by zero or entered something that's not an Int. Try again!")
println("Info from the exception: " + e.getMessage)
divide
}
}
Use future when your option is split over space–time continuum: right now it is empty, but seconds ago it is fulfilled (but not vice versa -- future is a process of fulfillment).
Future is new in Scala, just like Try and just like Try it made it's path from Twitter util. It used in concurrent code where you want to send some work to the background and either await result some time later or invoke a callback on completion. Try serves as a definite result -- future is completed successfully or ended up abnormally (caught an exception).
Note that there are many implementations of Future (Scalaz, Unfiltered, Twitter, Akka) -- they are likely will be unified with scala.actors.Future
Further reading is this brilliant overview of scala Futures/Promises.
Box should be used in Lift ecosystem and basically is an Option on steroids which as Empty/Full paths.
Or and Every. Or mimics Either and Box, Every is used to collect many errors.
Validation. Used from within Scalaz library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With