Почему Scala PartialFunction работает без определения isDefinedAt?

Похоже, что First и Second одинаковы, но почему?

Первый

val iter = List(1, 2, 3, 4, 5).iterator
val first = iter.collect(new PartialFunction[Int, Int]{
  def apply(i: Int) = i
  def isDefinedAt(i: Int) = i > 0 && i < 3
})
first.foreach((println(_)))

Второй

val iter2 = List(1, 2, 3, 4, 5).iterator
val second = iter2.collect {
  case i:Int if i > 0 && i < 3 => i
}
second.foreach((println(_)))

Это потому, что компилятор Scala автоматически преобразует { case i:Int if i > 0 && i < 3 => i } в форму реализации First с созданием isDefinedAt из части if i > 0 && i < 3?

Кроме того, case i:Int if i > 0 && i < 3 => i соответствует шаблону класса Case, если я прав. Однако в scala/src/library /scala/PartialFunction.scala, для PartialFunction нет определения класса Case.

trait PartialFunction[-A, +B] extends (A => B)

Тогда почему это совпадение с образцом класса case работает?

Я предполагаю, что компилятор Scala разумно выполняет множество неявных операций, но это сбивает меня с толку, чтобы понять, что происходит и как писать код Scala.

Если есть хорошие ссылки вместо спецификаций языка или компилятора, чтобы понять синтаксис кода Scala и способ написания кода Scala, пожалуйста, предложите.


person mon    schedule 16.11.2019    source источник
comment
Нет, сопоставление с образцом работает не только с классами case.   -  person Bergi    schedule 16.11.2019


Ответы (3)


Это потому, что компилятор Scala автоматически преобразует { case i:Int if i > 0 && i ‹ 3 => i } в форму реализации First с созданием isDefinedAt из части **if i > 0 && i ‹ 3 **?

Да, точный перевод приведен в Анонимные функции сопоставления с образцом. Вот это будет

new PartialFunction[Int, Int]{
  def apply(x: Int) = x match {
    case i:Int if i > 0 && i < 3 => i
  }
  def isDefinedAt(x: Int) = x match {
    case i:Int if i > 0 && i < 3 => true
    case _ => false
  } 
}

Обратите внимание на разницу с вашим первым примером в apply! Вы можете вызывать его, когда isDefined имеет значение false.

Кроме того, case i:Int, если i > 0 && i ‹ 3 => i соответствует шаблону класса Case, если я прав

Во всяком случае, это наоборот; классы case называются так, потому что они могут быть сопоставлены с образцом, а сопоставление с образцом использует ключевое слово case в Scala.

person Alexey Romanov    schedule 16.11.2019
comment
Спецификация там устарела, поэтому я разместил точный перевод. Я создам тикет, так как еще много времени, чтобы обновить спецификацию для Scala 3, у которой еще нет спецификации. Я также создам заявку на актуальную спецификацию, которую вы сможете прочитать для развлечения и получения прибыли. - person som-snytt; 17.11.2019

Да, компилятор преобразует вторую версию в PartialFunction[Int,Int] (потому что это то, что принимает collect).

Здесь нет соответствия case class, и оно даже не соответствует типу, потому что значение должно быть Int (поэтому объявление типа во второй версии не требуется).

руководство по стилю дает много советов о том, как обычно пишут на Scala.

person Tim    schedule 16.11.2019

Для вашего примера

object Main {
  def f = (1 to 5).collect { case i if i > 0 && i < 3 => i }
}

Сгенерированная компилятором частичная функция определяет applyOrElse, потому что она более эффективна, чем наивная идиома:

if (pf.isDefinedAt(x)) pf.apply(x) else ???

Показываю ту реализацию, которая похожа на то, что описано в спецификации:

$ scalac -Vprint:typer pf.scala
[[syntax trees at end of                     typer]] // pf.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    };
    def f: IndexedSeq[Int] = scala.Predef.intWrapper(1).to(5).collect[Int](({
      @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int] with java.io.Serializable {
        def <init>(): <$anon: Int => Int> = {
          $anonfun.super.<init>();
          ()
        };
        final override def applyOrElse[A1 <: Int, B1 >: Int](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
          case (i @ _) if i.>(0).&&(i.<(3)) => i
          case (defaultCase$ @ _) => default.apply(x1)
        };
        final def isDefinedAt(x1: Int): Boolean = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
          case (i @ _) if i.>(0).&&(i.<(3)) => true
          case (defaultCase$ @ _) => false
        }
      };
      new $anonfun()
    }: PartialFunction[Int,Int]))
  }
}

где AbstractPartialFunction определяет

def apply(x: T1): R = applyOrElse(x, PartialFunction.empty)

Вот внешняя ссылка на изменение использования applyOrElse. улучшенная функция PartialFunction появилась в 2012 году. рекламируется. Некоторую информацию можно получить, развернув Scaladoc для PartialFunction. По какой-то причине эта ссылка показывает orElse, поэтому вам действительно придется прокрутить назад до applyOrElse. Кажется, документация сложна.

person som-snytt    schedule 17.11.2019