Как бы я реализовать расширенную функцию на Function1 или PartialFunction

Я хотел бы определить расширенную функцию на Function1 или PartialFunction.

Я хочу сделать это, потому что у меня есть сценарий использования, подобный следующему:

class A
class B extends A

def foo(fun: Function[B, A]) = {
    bar(fun.widen[A])
}

def bar(pf: PartialFunction[A, A]) = ???

Как видно выше, для достижения этой цели я подумал об определении расширенной функции, такой как эта:

implicit class AugmentedFunction[T, U](fun: T => U) {
    def widen[T1 >: T]: PartialFunction[T1, U] = { case t: T => fun(t) }
}

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

Пояснение:когда я говорю, что это не работает, я имею в виду, что оно генерирует исключение при использовании (см. Фрагмент кода ScalaKata), когда оно на самом деле не должно генерировать исключение и печатать "не определено" в конкретном случае фрагмента кода в ScalaKata.

Мой вопрос:

Как я могу решить эту проблему правильно? В Scalaz или Shapeless уже есть такая функциональность, о которой я не знаю? Есть ли смысл делать это в первую очередь?

Вот фрагмент со всем кодом: http://www.scalakata.com/527bb729e4b0b1a1c4db1a73

3 ответа

Я думаю, что вы можете сделать это с помощью манифестов классов:

implicit class AugmentedFunction[T, U](fun: T => U)(implicit m: Manifest[T]) {
  def widen[T1](implicit m1: Manifest[T1]): PartialFunction[T1, U] = {
    case a if(m <:< m1) => fun(a.asInstanceOf[T]) 
  }
}

class A
class B extends A
class C

val theFun: B => A = (b: B) => b

theFun.widen[A].isDefinedAt(new B) // true
theFun.widen[C].isDefinedAt(new C) // false

Ну ошибка говорит обо всем java.lang.ClassCastException: ScalaKata$A$1 cannot be cast to ScalaKata$B$1, В основном, когда вы звоните foo( (a: B) => new A, new A) A значение передается лямбда, которая на самом деле принимает тип B в качестве параметра и, следовательно, исключение при кастинге из A в B так как это невозможно привести от общего к конкретному.

Вы должны использовать что-то вроде:

foo( (a: Ai) => new Ai, new Ai)
foo( (a: Ci) => new Ai, new Ci)
foo( (a: Ai) => new Ai, new Bi)

По сути, 2-й параметр должен быть подтипом типа лямбда-аргумента.

Реализация с использованием TypeTag (так как Manifest устарела)

import scala.reflect.runtime.universe._

implicit class AugmentedFunction[T : TypeTag, U](fun: T => U) {
  def widen[T1 : TypeTag]: PartialFunction[T1, U] = {
    case a if typeOf[T] <:< typeOf[T1] => fun(a.asInstanceOf[T])
  }
} 
Другие вопросы по тегам