Почему замена моего класса дел Scala на экстрактор сломала мою функцию более высокого порядка? - PullRequest
5 голосов
/ 24 января 2011

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

case class Wrapper(x :Int)
def higherOrder(f : Int => Wrapper) = println(f(42))

Затем я могу вызвать функцию более высокого порядка, передавсгенерированная функция применения оболочки.Удивительно, но я также могу просто указать имя оболочки.

higherOrder(Wrapper.apply)  // okay
higherOrder(Wrapper)        // okay, wow!

Это действительно круто.Это позволяет нам рассматривать имя класса case как функцию, которая способствует выразительным абстракциям.Пример этой крутости смотрите в ответе здесь. Что означает «абстракция поверх»?

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

// Replace Wrapper case class with an extractor
object Wrapper {
    def apply(x :Int) = new Wrapper(x)
    def unapply(s :String) :Option[Wrapper] = {
        // details elided
    }
}
class Wrapper(x :Int) {
    override def toString = "Wrapper(" + x + ")"
    // other methods elided
}

При этом изменении я все еще могу передать Wrapper.apply в свою функцию более высокого порядка,но передача только Обертки больше не работает.

higherOrder(Wrapper.apply)  // still okay
higherOrder(Wrapper)        // DOES NOT COMPILE
            ^^^^^^^
//    type mismatch; found : Wrapper.type (with underlying type Wrapper)
//    required: (Int) => Wrapper

Ой!Вот почему эта асимметрия вызывает беспокойство.Совет Одерского, Ложки и Веннерса (Программирование на Scala, стр. 500) гласит:

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

Совершенно верно, конечно, но мы сломаем наших клиентов, если онииспользуя имена классов в качестве функций.И поскольку это позволяет использовать мощные абстракции, некоторые наверняка это сделают.

Итак, передавая их в функции более высокого порядка, как мы можем заставить экстракторы вести себя так же, как классы case?

Ответы [ 2 ]

12 голосов
/ 24 января 2011

Проще говоря, Wrapper (объект) не является функцией, это просто объект с методом применения. Это не имеет абсолютно никакого отношения к объекту, который также является экстрактором.

Попробуйте это:

class Wrapper(val x: Int) {
  override def toString = "Wrapper(" + x + ")"
  // other methods elided
}

object Wrapper extends (Int => Wrapper) {
  def apply(x: Int) = new Wrapper(x)
  def unapply(w: Wrapper): Option[Int] = Some(w.x)
}

def higherOrder(f: Int => Wrapper) = println( f(42) )

Я также присвоил параметру Wrapper a val и поменял местами параметр и возвращаемое значение в вашем определении unapply, чтобы оно соответствовало поведению класса случая.

8 голосов
/ 24 января 2011

Сопутствующие объекты экстрактора Scala должны расширять. Функция

Для классов case компилятор генерирует сопутствующий объект под капотом.Это содержит соответствующие статические методы для класса case Wrapper, включая apply.Вглядываясь в байт-код, мы обнаруживаем, что сопутствующий объект Wrapper расширяет Function1.(На самом деле он расширяет AbstractFunction1, которая использует @specialized для устранения автобокса.)

Это также отмечено здесь. Почему сопутствующие объекты класса case расширяют FunctionN?

При замене нашего класса case Wrapper на экстрактор, мы должны позаботиться о расширении нашего домашнего дополненного объекта AbstractFunction1, чтобы сохранить обратную совместимость для нашегоклиенты.

Это составляет небольшую настройку в исходном коде.Метод применения не меняется вообще.

object Wrapper extends scala.runtime.AbstractFunction1[Int, Wrapper] {
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...