Чередование частичных функций с andThen в Scala

Давайте использовать примеры из Daily Scala:

type PF = PartialFunction[Int,Int]

val pf1 : PF = {case 1 => 2}                      

val pf2 : PF = {case 2 => 3}                      

и давайте добавим:

val pf3 : PF = {case 3 => 4}

и тогда работает как положено здесь:

pf1 andThen pf2 isDefinedAt(x)

возвращается true тогда и только тогда x == 1 (на самом деле, pf2 не должен быть PartialFunction вообще)

Однако я ожидал, что:

pf1 andThen pf3 isDefinedAt(x)

вернется false для всех x (т. е. если pf1 определен, проверьте pf3), но он не проверяет и только проверяет pf1.

В конце, pf1 andThen pf3 lift(x) всегда приводить к MatchError. Я предпочел бы получить None... Я могу получить это поведение, подняв каждую функцию, например, в pf1.lift(x).flatMap(pf3.lift) но есть ли более простой способ использования чистого API PartialFunction? (и не поднимая каждую частичную функцию в отдельности?)

3 ответа

Решение

Если вы посмотрите на andThen:

def andThen[C](k: (B) => C): PartialFunction[A, C]

Это составляет приемник с функцией, а не частичной функцией. То есть, k как ожидается, будет полностью определен, он не имеет isDefinedAt, Следовательно, результирующая частичная функция не должна изменять поведение isDefinedAt, он все еще просто должен обратиться к первой частичной функции.

Вы можете написать собственное расширение, которое состоит из двух частичных функций:

implicit class ComposePartial[A, B](pf: PartialFunction[A, B]) {
  def collect[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
    new PartialFunction[A, C] {
      def apply(a: A): C = that(pf(a))
      def isDefinedAt(a: A) = pf.isDefinedAt(a) && {
        val b = pf(a)
        that.isDefinedAt(b)
      }
    }
}

pf1 collect pf2 isDefinedAt(1)  // true
pf1 collect pf3 isDefinedAt(1)  // false

Проблема в том, что вы должны вызвать pf(a)Таким образом, учитывая, что Scala не обеспечивает чистоту, вы можете в конечном итоге выполнять побочные эффекты нежелательно.

Вам нужен эквивалент flatMap за PartialFunctions.

implicit class CollectPartial[A, B](f: PartialFunction[A, B]) {
    def collect[C](g: PartialFunction[B, C]) = Function.unlift { a: A =>
        f.lift(a).flatMap(g.lift)
    }
}

Используйте это как

val a: PartialFunction[String, Int] = ...
val b: PartialFunction[Int, Char] = ...
val c: PartialFunction[String, Char] = a collect b

Это работает, как ожидается, даже с побочными эффектами.

Почему бы просто:

def compose[A,B,C](f: PartialFunction[A, B], g: PartialFunction[B, C]) : PartialFunction[A, C] =
Function.unlift(f.andThen(g.lift))
Другие вопросы по тегам