Синтаксис анонимной частичной функции

Я задавал этот вопрос ранее: Объединить PartialFunction с обычной функцией

а потом понял, что на самом деле я не правильно спросил. Итак, вот еще одна попытка.

Если я сделаю это:

 val foo = PartialFunction[Int, String] { case 1 => "foo" }
 val bar = foo orElse { case x => x.toString }

не компилируется: error: missing parameter type for expanded function The argument types of an anonymous function must be fully known. (SLS 8.5) Expected type was: PartialFunction[?,?]

Но это отлично работает:

   val x: Seq[String] = List(1,2,3).collect { case x => x.toString }

Вопрос в чем разница? Тип аргумента в обоих случаях одинаков: PartialFunction[Int, String]. Передаваемое значение буквально идентично. Почему в одном случае работает, а в другом нет?


person Dima    schedule 13.07.2016    source источник
comment
collect ожидает PartialFunction[A, B], а orElse ожидает [A1 <: A, B1 >: B] PartialFunction[A1, B1], поэтому, хотя компилятор может вывести первое, вам как-то нужно помочь со вторым.   -  person Peter Neyens    schedule 13.07.2016
comment
Спасибо, @PeterNeyens, это объясняет для меня. Опубликуйте это как ответ, если вам небезразличны 25 баллов. ударяться :)   -  person Dima    schedule 14.07.2016
comment
Вы можете добавить ответ @PeterNeyens и принять его, чтобы его можно было легко найти, и людям не нужно было просматривать комментарии, как это сделал я.   -  person MaxNevermind    schedule 06.08.2018


Ответы (3)


Вам нужно указать тип для bar, потому что компилятор не может его вывести. Это компилирует:

val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar : (Int => String) = foo orElse { case x => x.toString }
person henrik    schedule 13.07.2016
comment
В этом случае метод orElse, который вызывается, находится в Function, а не в PartialFunction. Это иллюстрирует еще одну двусмысленность вокруг этого, которую вы не хотели бы, чтобы компилятор просто предполагал. Я не уверен, что ОП в конце концов хочет функцию. - person Scott Shipp; 13.07.2016
comment
@ScottShipp ты уверен? где orElse в Function? И что он делает? - person Dima; 13.07.2016
comment
@henrik, я понимаю, что это невозможно сделать. Вопрос заключался в том, почему он не может вывести это, и почему он может сделать вывод в другом случае. - person Dima; 13.07.2016
comment
@Dima orElse — это метод в признаке PartialFunction и имеет границы типа [A1 <: A, B1 >: B], в данном случае A — это Int, а B — это String. Поскольку B1 ограничивается только снизу типом String, он не может вывести фактический тип, который вам нужен. - person henrik; 13.07.2016
comment
Да, я понял это из комментария @PeterNeyens ранее. Жаль, что он не опубликовал это как ответ :) - person Dima; 14.07.2016

В случае List(1,2,3).collect{case x => x.toString} компилятор может вывести тип ввода частичной функции на основе того, как был введен List.

final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That])

На основе параметров типа компилятор может сделать вывод, что вы передаете правильно типизированную частичную функцию. Вот почему List(1,2,3).collect{case x:String => x.toString} не компилируется, как и List(1,2,3).collect{case x:Int => x.toString; case x: String => x.toString}.

Поскольку List является ковариантным, компилятор может сделать вывод, что частичная функция {case x => x.toString} является частичной функцией для Int. Вы заметите, что List(1,2,3).collect{case x => x.length} не компилируется, потому что компилятор делает вывод, что вы работаете либо с Int, либо с подклассом Int.

Также имейте в виду, что {case x => x.toString} — это просто синтаксический сахар. Если мы сделаем что-то вроде приведенного ниже, ваш пример будет работать так, как ожидалось.

val f = new PartialFunction[Int, String](){
  override def isDefinedAt(x: Int): Boolean = true
  override def apply(v1: Int): String = v1.toString
}

val foo = PartialFunction[Int, String] { case 1 => "foo" }

val bar = foo orElse f //This compiles fine.

List(1,2,3).collect{f} // This works as well.

Таким образом, единственный логичный ответ, с моей точки зрения, заключается в том, что синтаксический сахар, способный генерировать экземпляр PartialFunction для {case x => x.toString}, не имеет достаточно информации во время компиляции, чтобы иметь возможность адекватно ввести его как PartialFunction[Int, String] в вашем случае orElse.

person nattyddubbs    schedule 13.07.2016
comment
Это не идеальное решение, потому что вы не реализовали applyOrElse. applyOrElse улучшит производительность. - person Yang Bo; 03.09.2016

Вы можете использовать библиотеку Extractor.scala.

import com.thoughtworks.Extractor._

// Define a PartialFunction
val pf: PartialFunction[Int, String] = {
  case 1 => "matched by PartialFunction"
}

// Define an optional function
val f: Int => Option[String] = { i =>
  if (i == 2) {
    Some("matched by optional function")
  } else {
    None
  }
}

// Convert an optional function to a PartialFunction
val pf2: PartialFunction[Int, String] = f.unlift

util.Random.nextInt(4) match {
  case pf.extract(m) => // Convert a PartialFunction to a pattern
    println(m)
  case f.extract(m) => // Convert an optional function to a pattern
    println(m)
  case pf2.extract(m) => // Convert a PartialFunction to a pattern
    throw new AssertionError("This case should never occur because it has the same condition as `f.extract`.")
  case _ =>
    println("Not matched")
}
person Yang Bo    schedule 03.09.2016