Suppose I’m writing a GUI

class Kitteh (val age: Int) {
require (age < 5)
def saveMeow(file: File) = { /* implementation */ }
def savePurr(file: File) = { /* implementation */ }
}
The frame has a field for the current Kitteh, which is an Option because it might not have been defined yet, or the user may have attempted to create an invalid one:
var currentKitteh: Option[Kitteh] = None
Now I want to create a Kitteh safely when the user hits Create
val a = ... // parse age from text box
currentKitteh = try { Some(new Kitteh(a)) } catch { case _ => None }
My GUI has two buttons which do similar things. In psedocode, they should both
if (currentKitteh.isDefined) {
if (file on the disk already exists) {
bring up a dialog box asking for confirmation
if (user confirms)
<< execute method on currentKitteh >>
}
}
else bring up warning dialog
Don’t worry about the detail: the point is that because there is code duplication, I want to create a common method that I can call from both buttons. The only difference is the method on the Kitteh that needs to be executed.
Now if currentKitteh were not an Option, the common method could have a signature like
def save(filename: String, f:(File => Unit)) {
which I could call with, for example
save("meow.txt", currentKitteh.saveMeow _)
but since it is actually an Option, how could I implement this?
I could just check whether currentKitteh is defined, and do a .get before calling the save method for each button, but is there another way, leaving this check in the save method? In other words, given an Option[A], is it possible to specify a partial function from a method on the (possibly non-existent) A object?
(hope this question makes sense, convoluted example notwithstanding)
edit: Bonus question: what if, instead of Option[Kitteh], I used Either[Throwable, Kitteh]?
update: Additional line added to pseudocode to bring up warning dialog: ideally, the save method should always be called so that the user is warned if there is no valid Kitteh to save.
This looks like the best option to me:
If you’re repeatedly doing this, you can abstract it,
I suppose to answer your original question, you could also push the logic into the function argument,
The semantics are a little different with this version.
Update. If
k: Option[Kitteh]is replaced byk: Either[Throwable, Kitteh], then what aboutk.right foreach { c => ... }? You could also usek.right map ...if you want to preserve error information.In response to the modified question, here’s another abstraction possibility,
Now
savehas the responsibility of unpackingcurrentKitteh. Callsavelike this,or like this,