Создание частично примененной функции из метода типа в Option - PullRequest
3 голосов
/ 09 сентября 2011

Предположим, я пишу GUI

enter image description here

class Kitteh (val age: Int) {
  require (age < 5)
  def saveMeow(file: File) = { /* implementation */ }
  def savePurr(file: File) = { /* implementation */ }
}

В кадре есть поле для текущего Kitteh, которое является Option, потому что оно могло не бытьеще не определено, или пользователь, возможно, пытался создать недействительный:

var currentKitteh: Option[Kitteh] = None

Теперь я хочу безопасно создать Kitteh, когда пользователь нажимает Создать

val a = ... // parse age from text box
currentKitteh = try { Some(new Kitteh(a)) } catch { case _ => None }

Мой графический интерфейсимеет две кнопки, которые делают похожие вещи.В psedocode они оба должны:

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

Не беспокойтесь о деталях: дело в том, что из-за дублирования кода я хочу создать общий метод, который я могу вызывать с обеих кнопок.Единственное отличие - это метод на Kitteh, который должен быть выполнен.

Теперь, если currentKitteh не был Option, общий метод мог бы иметь подпись типа

def save(filename: String, f:(File => Unit)) {

который я мог бы вызвать, например,

save("meow.txt", currentKitteh.saveMeow _)

, но так как это на самом деле Option, как я мог бы реализовать это?

Я мог бы просто проверить, определен ли currentKitteh, и выполнить .get до вызова метода save для каждой кнопки, но есть ли другой способ оставить эту проверку в методе save?Другими словами, учитывая Option[A], возможно ли указать частичную функцию из метода объекта (возможно, несуществующего) A?

(надеюсь, что этот вопрос имеет смысл, несмотря на извилистый пример))

edit : Бонусный вопрос: а что если вместо Option[Kitteh] я использовал Either[Throwable, Kitteh]?

update : добавлена ​​дополнительная строкапсевдокод, чтобы вызвать диалоговое окно с предупреждением: в идеале всегда следует вызывать метод save, чтобы пользователь предупреждался, если для сохранения нет действительного Kitteh.

Ответы [ 3 ]

4 голосов
/ 09 сентября 2011

Для меня это лучший вариант:

currentKitteh foreach { c => save("meow.txt", c.saveMeow _) }

Если вы неоднократно делаете это, вы можете абстрагироваться,

def currentSaveMeow(file: String) = currentKitteh foreach { c =>
  save(file, c.saveMeow _)
}
currentSaveMeow("meow.txt")

Полагаю, что для ответа на исходный вопрос, вы также можете вставить логику в аргумент функции,

save("meow.txt", file => currentKitten.foreach(_.saveMeow(file)))

Семантика немного отличается в этой версии.

Обновление . Если k: Option[Kitteh] заменен на k: Either[Throwable, Kitteh], то как насчет k.right foreach { c => ... }? Вы также можете использовать k.right map ..., если хотите сохранить информацию об ошибках.


В ответ на измененный вопрос вот еще одна возможность абстракции,

def save(filename: String, f: (Kitteh, File) => Unit)

Теперь save несет ответственность за распаковку currentKitteh. Звоните save вот так

save("meow.txt", (k, f) => k.saveMeow(f))

или как это,

save("meow.txt", _ saveMeow _)
0 голосов
/ 10 сентября 2011

Вы можете определить класс BadKitteh и получить сообщения об ошибках.Затем просто используйте currentKitty.getOrElse (badKitty), если он вам нужен.

0 голосов
/ 09 сентября 2011

Вы можете сопоставить функцию с ней и получить функцию сбоя:

def save = 
  o map {s => () => "saved a kitteh! " + s} getOrElse {() => "oh noes, no kittehs!"}

, тогда вы просто:

save()
...