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:
currentKitteh foreach { c => save("meow.txt", c.saveMeow _) }
If you're repeatedly doing this, you can abstract it,
def currentSaveMeow(file: String) = currentKitteh foreach { c =>
save(file, c.saveMeow _)
}
currentSaveMeow("meow.txt")
I suppose to answer your original question, you could also push the logic into the function argument,
save("meow.txt", file => currentKitten.foreach(_.saveMeow(file)))
The semantics are a little different with this version.
Update. If k: Option[Kitteh]
is replaced by k: Either[Throwable, Kitteh]
, then what about k.right foreach { c => ... }
? You could also use k.right map ...
if you want to preserve error information.
In response to the modified question, here's another abstraction possibility,
def save(filename: String, f: (Kitteh, File) => Unit)
Now save
has the responsibility of unpacking currentKitteh
. Call save
like this,
save("meow.txt", (k, f) => k.saveMeow(f))
or like this,
save("meow.txt", _ saveMeow _)