Класс кейса и линеаризация признаков - PullRequest
10 голосов
/ 14 марта 2012

Предположим, что я хочу написать класс кейсов Stepper следующим образом:

case class Stepper(step: Int) {def apply(x: Int) = x + step}

Он поставляется с хорошей реализацией toString:

scala> Stepper(42).toString
res0: String = Stepper(42)

, но это не совсемфункция:

scala> Some(2) map Stepper(2)
<console>:10: error: type mismatch;
 found   : Stepper
 required: Int => ?
              Some(2) map Stepper(2)

Обходной путь должен реализовать черту Function ...

case class Stepper(step: Int) extends (Int => Int) {def apply(x: Int) = x + step}

Но тогда я не могу больше бесплатно иметь хорошую реализацию toString:

scala> Stepper(42).toString
res2: java.lang.String = <function1>

Тогда возникает вопрос: могу ли я получить лучшее из этих двух миров?Есть ли решение, где у меня есть хорошая toString реализация бесплатно И реализация черты Function.Другими словами, есть ли способ применить линеаризацию таким образом, чтобы case class синтаксический сахар был применен наконец?

Ответы [ 3 ]

8 голосов
/ 14 марта 2012

Вопрос не имеет отношения к линеаризации.В case-классах toString - это метод, автоматически генерируемый компилятором, если и только если Any.toString не переопределяется в типе конца.

Однако ответ частично связан с линеаризацией - нам нужно переопределить Function1.toString с помощью метода, который был бы сгенерирован компилятором, если бы не версия, представленная Function1:

trait ProperName extends Product {
  override lazy val toString = scala.runtime.ScalaRunTime._toString(this)
}

// now just mix in ProperName and... magic!
case class Stepper(step: Int) extends (Int => Int) with ProperName {
  def apply(x:Int) = x+step
}

Тогда

println(Some(2) map Stepper(2))
println(Stepper(2))

Будет производить

Some(4)
Stepper(2)

Обновление

Вот версия ProperName черта, не основанная на недокументированном методе API:

trait ProperName extends Product {
  override lazy val toString  = {
    val caseFields = {
       val arity = productArity
       def fields(from: Int): List[Any] =
         if (from == arity) List()
         else productElement(from) :: fields(from + 1)
       fields(0) 
    }
    caseFields.mkString(productPrefix + "(", ",", ")")
  }
}

Альтернативная реализация toString получена из исходного кода для исходного метода _toString scala.runtime.ScalaRunTime._toString.

Обратите внимание, что эта альтернативная реализация по-прежнему основана на предположении, что класс case всегда расширяет черту Product.Хотя последнее относится к Scala 2.9.0 и является фактом, который известен и на который полагаются некоторые члены сообщества Scala, он официально не задокументирован как часть Scala Language Spec .

2 голосов
/ 14 марта 2012

РЕДАКТИРОВАТЬ: Как насчет переопределения toString?

case class Stepper(step: Int) extends (Int => Int) {
  def apply(x: Int) = x + step
  override def toString = "Stepper(" + step + ")"
}
1 голос
/ 14 марта 2012

Вы можете использовать неявное преобразование, чтобы Stepper обрабатывалось как функция, только когда это необходимо:

case class Stepper(step: Int) { def apply(x: Int) = x + step }

implicit def s2f(s: Stepper) = new Function[Int, Int] {
  def apply(x: Int) = s.apply(x)
}

Теперь вы получаете toString класса наблюдения при вызове Stepper(42).toString, но Some(2) map Stepper(2) также работает по желанию.

(обратите внимание, что я был более многословен, чем необходимо выше, чтобы не усложнять механику. Вы также можете написать implicit def s2f(s: Stepper) = s.apply _ или любое количество других более кратких формулировок).

...