La production d'une fonction partiellement appliquée de procédé de type dans une option
-
28-10-2019 - |
Question
Supposons que j'écris une interface graphique
class Kitteh (val age: Int) {
require (age < 5)
def saveMeow(file: File) = { /* implementation */ }
def savePurr(file: File) = { /* implementation */ }
}
Le cadre a un champ pour le Kitteh actuel, qui est un Option
parce qu'il pourrait ne pas avoir encore été défini, ou l'utilisateur peut avoir tenté de créer un un invalide:
var currentKitteh: Option[Kitteh] = None
Maintenant, je veux créer un Kitteh
en toute sécurité lorsque l'utilisateur clique Créer
val a = ... // parse age from text box
currentKitteh = try { Some(new Kitteh(a)) } catch { case _ => None }
GUI Mon a deux boutons qui font des choses semblables. En psedocode, ils doivent à la fois
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
Ne vous inquiétez pas au sujet du détail: le point est que parce qu'il ya duplication de code, je veux créer une méthode commune que je peux appeler des deux boutons. La seule différence est la méthode sur la Kitteh qui doit exécuter.
Maintenant, si currentKitteh
n'était pas un Option
, la méthode commune pourrait avoir une signature comme
def save(filename: String, f:(File => Unit)) {
que je pourrais appeler, par exemple
save("meow.txt", currentKitteh.saveMeow _)
mais comme il est en fait une option, comment pourrais-je mettre en œuvre cette?
Je pourrais vérifier si currentKitteh est défini, et faire un .get
avant d'appeler la méthode save
pour chaque bouton, mais est-il une autre façon, en laissant cette vérification dans la méthode save
? En d'autres termes, étant donné un Option[A]
, il est possible de spécifier une fonction partielle d'une méthode sur le (éventuellement nulle) objet A
?
(espérons que cette question est logique, par exemple alambiquée malgré)
modifier : Question bonus: si, au lieu de Option[Kitteh]
, je Either[Throwable, Kitteh]
mise à jour : Ligne supplémentaire ajouté à pseudocode pour faire apparaître de dialogue d'avertissement. Idéalement, la méthode save
doit toujours être appelé afin que l'utilisateur est averti s'il n'y a pas Kitteh valide pour sauver
La solution
Cela ressemble à la meilleure option pour moi:
currentKitteh foreach { c => save("meow.txt", c.saveMeow _) }
Si vous faites à plusieurs reprises, vous pouvez abstraire,
def currentSaveMeow(file: String) = currentKitteh foreach { c =>
save(file, c.saveMeow _)
}
currentSaveMeow("meow.txt")
Je suppose que pour répondre à votre question initiale, vous pouvez aussi pousser la logique dans l'argument de la fonction,
save("meow.txt", file => currentKitten.foreach(_.saveMeow(file)))
La sémantique est un peu différent avec cette version.
Mise à jour . Si k: Option[Kitteh]
est remplacé par k: Either[Throwable, Kitteh]
, alors qu'en est-k.right foreach { c => ... }
? Vous pouvez également utiliser k.right map ...
si vous souhaitez conserver des informations d'erreur.
En réponse à la question modifiée, voici une autre possibilité d'abstraction,
def save(filename: String, f: (Kitteh, File) => Unit)
save
a la responsabilité de déballer currentKitteh
. Appel save
comme ça,
save("meow.txt", (k, f) => k.saveMeow(f))
ou comme celui-ci,
save("meow.txt", _ saveMeow _)
Autres conseils
Vous pouvez mapper une fonction et getOrElse votre fonction échouer:
def save =
o map {s => () => "saved a kitteh! " + s} getOrElse {() => "oh noes, no kittehs!"}
vous simplement:
save()
Vous pourriez définir une classe BadKitteh et ont produit que des messages d'erreur. Ensuite, utilisez simplement currentKitty.getOrElse (badkitty) si vous en avez besoin.