(Scala) Am I using Options correctly?


(Scala) Am I using Options correctly?



I'm currently working on my functional programming - I am fairly new to it. Am i using Options correctly here? I feel pretty insecure on my skills currently. I want my code to be as safe as possible - Can any one point out what am I doing wrong here or is it not that bad? My code is pretty straight forward here:


def main(args: Array[String]): Unit =
{
val file = "myFile.txt"
val myGame = Game(file) //I have my game that returns an Option here

if(myGame.isDefined) //Check if I indeed past a .txt file
{
val solutions = myGame.get.getAllSolutions() //This returns options as well
if(solutions.isDefined) //Is it possible to solve the puzzle(crossword)
{
for(i <- solutions.get){ //print all solutions to the crossword
i.solvedCrossword foreach println
}
}
}
}



-Thanks!! ^^





Why does getAllSolutions return an option? Is there a semantic difference between it returning Some(Nil) or None? I mean, your code treats both the same (by doing nothing), but maybe that's just a simplified version of your real code. Also am I understanding correctly that Game() returns None only in case of errors (such as the file not existing or you not having permission to read it or some IO failure)? If so, it's probably not the best solution to represent all those error cases as None - that loses information.
– sepp2k
Jul 1 at 14:34



getAllSolutions


Some(Nil)


None


Game()


None


None





getAllSolutions return an option because if there are no solutions I would return 'None'. Game() returns 'None' as well when a file is in not correct format. Now If i return a class, right, If i know that there is an error and i want to return 'None' What else should i return? A string that explains my error? But if I have Option[myClass] - I dont want to return some string that explains my error - Do I? Thank you!
– JoeDortman
Jul 1 at 14:41





But why doesn't getAllSolutions just return an empty list of solutions if there are no solutions? Are there cases where you'd return Some(Nil) and are there situations where you'd treat Some(Nil) differently than None?
– sepp2k
Jul 1 at 14:47


getAllSolutions


Some(Nil)


Some(Nil)


None





Okey, I get it. I should just return an empty list of solutions. And for the other question, if I'm getting it right - I never use Nil expressions. I think you should never do that. Thank you so much for help! ^^
– JoeDortman
Jul 1 at 15:04





Clarification: Nil is the empty list (not to be confused with null). It's the same as List(). Sorry if that was confusing.
– sepp2k
Jul 1 at 15:07


Nil


null


List()




3 Answers
3



As a rule of thumb, you can think of Option as a replacement for Java's null pointer. That is, in cases where you might want to use null in Java, it often makes sense to use Option in Scala.


Option


null


Option



Your Game() function uses None to represent errors. So you're not really using it as a replacement for null (at least I'd consider it poor practice for an equivalent Java method to return null there instead of throwing an exception), but as a replacement for exceptions. That's not a good use of Option because it loses error information: you can no longer differentiate between the file not existing, the file being in the wrong format or other types of errors.


Game()


None


null


null


Option



Instead you should use Either. Either consists of the cases Left and Right where Right is like Option's Some, but Left differs from None in that it also takes an argument. Here that argument can be used to store information about the error. So you can create a case class containing the possible types of errors and use that as an argument to Left. Or, if you never need to handle the errors differently, but just present them to the user, you can use a string with the error message as the argument to Left instead of case classes.


Either


Either


Left


Right


Right


Option


Some


Left


None


Left


Left



In getAllSolutions you're just using None as a replacement for the empty list. That's unnecessary because the empty list needs no replacement. It's perfectly fine to just return an empty list when there are no solutions.


getAllSolutions


None



When it comes to interacting with the Options, you're using isDefined + get, which is a bit of an anti pattern. get can be used as a shortcut if you know that the option you have is never None, but should generally be avoided. isDefined should generally only be used in situations where you need to know whether an option contains a value, but don't need to know the value.


Option


isDefined


get


get


None


isDefined



In cases where you need to know both whether there is a value and what that value is, you should either use pattern matching or one of Option's higher-order functions, such as map, flatMap, getOrElse (which is kind of a higher-order function if you squint a bit and consider by-name arguments as kind-of like functions). For cases where you want to do something with the value if there is one and do nothing otherwise, you can use foreach (or equivalently a for loop), but note that you really shouldn't do nothing in the error case here. You should tell the user about the error instead.


Option


map


flatMap


getOrElse


foreach


for





I don't think Try is a good alternative either since its left side only encodes exceptions. Only use that when calling Java API that throw exceptions. I would use Either instead and create a custom error hierarchy (or start simple and use String for the left side) instead.
– cbley
Jul 1 at 16:19


Try


Either


String





@cbley You're right. Either's better.
– sepp2k
Jul 1 at 16:27


Either





Wow! Thank you for such a detailed explanation! +^^
– JoeDortman
Jul 2 at 10:48



Instead of the java style for loop, use higher-order function:


myGame match {
case Some(allSolutions) =>
val solutions = allSolutions.getAllSolutions
solutions.foreach(_.solvedCrossword.foreach(println))
case None =>
}





Thank you so much for sharing this with me! ^^
– JoeDortman
Jul 1 at 14:06



If all you need here is to print it in case all is good, you can use for-comprehension which is considered quite idiomatic Scala way


for {
myGame <- Game("mFile.txt")
solutions <- myGame.getAllSolutions()
solution <- solutions
crossword <- solution.solvedCrossword
} println(crossword)






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to add background colour in existing image using Swift?

Moria Casán

How to make file upload 'Required' in Contact Form 7?