Как бы я реализовать расширенную функцию на 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])
}
}