Как проверить, является ли функция частичной в Scala? - PullRequest
1 голос
/ 03 апреля 2020

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

def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
  f match {
    case p:PartialFunction[X,Y]=> opt.flatMap(p.lift) //This doesn't seem to work
    case _ => opt.map(f)
  }
}

Таким образом, я могу использовать метод это

doSomething(x){
case t if predicate(t) =>  otherMethod(t)
}

, поэтому, если у меня нет предиката, я могу использовать его вот так doSomething(x)(otherMethod) вместо

doSoemthing(x){
case t=> otherMethod(t)
}

Примечание: ищите решение, которое не т требует отлова MatchError исключения

Ответы [ 3 ]

2 голосов
/ 03 апреля 2020

Это не ответ, потому что я не думаю, что то, что вы хотите, возможно в Scala.

Исходный метод в порядке и работает, как и ожидалось, хотя он может быть немного проще:

def doSomething[X, Y](opt: Option[X])(f: X => Y): Option[Y] = {
  f match {
    case p: PartialFunction[X, Y] => opt.collect(p)
    case _ => opt.map(f)
  }
}

Проблема здесь:

doSomething(x){
  case t if predicate(t) =>  otherMethod(t)
}

Scala создает Function вместо PartialFunction из этого выражения match, поэтому тест не пройден. Если вы передаете реальное PartialFunction, метод работает нормально.

val p: PartialFunction[Int, Int] = {
  case i: Int if i > 0 => i
}

doSomething(Some(0))(p) // Returns None

Я не думаю, что есть какой-либо способ сделать то, что вы хотите, в основном потому, что doSomething имеет несколько списков аргументов, которые портят тип вычет для второго списка аргументов.

Я предлагаю просто использовать

x.map(f)

или

x.collect{
  case ...
}

в зависимости от кода вызова.

1 голос
/ 03 апреля 2020

Синтаксис для частичной функции был изменен с 2.9 в SLS 8.5, поэтому даже если вы выполните { case x => y}, это НЕ означает, что это частичная функция. Его тип будет точным, так как вы определите его как.

В вашем случае вы определили его как X=>Y (как в параметре вашей функции), так что это просто X=>Y (он был скомпилирован в обычная функция и регистры без совпадения будут вызывать MatchError), и даже если вы сделаете isInstanceOf[PartialFunciton[_,_]], он не будет совпадать.

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

doSomething(Some(1))({case 2 => 0}: PartialFunction[Int,Int]) //This returns None without MatchError

, а

doSomething(Some(1)){case 2 => 0} //This gives MatchError and it is not recognized as PartialFunction inside the body

Возможно, это не так удобно, как вы думали, но это единственный способ заставить его работать. (или вы определяете 2 отдельные функции для каждого случая, например collect и map в стандартной библиотеке)

0 голосов
/ 03 апреля 2020

Я не уверен, что вы передаете в качестве частичной функции, но определенно вам следует определить ее с помощью конкретной c сигнатуры, например:

val positive: PartialFunction[Int, Option[Int]] = {
  case x if x >= 0 => Some(x)
  case _ => None

Функция positive определена только для положительных чисел. В случае отрицательных чисел функция возвращает None, и вы не получите scala .MatchError во время выполнения.

Эта специфицированная c функция позволяет вам получить доступ к методу isDefinedAt, который динамически тестируется, если значение находится в домене функции.

postive (5) .isDefinedAt // true

poistive.isInstanceOf [PartialFunction [Int, Option [Int]]] // true

Я продемонстрировал здесь, почему вы всегда получаете ложь, когда проверяете p.isInstanceOf

def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
  f match {
    case p if p.isInstanceOf[PartialFunction[X,Y]] =>
    println("I'm a pf")
    println(s"Is it PartialFunction: ${p.isInstanceOf[PartialFunction[X,Y]]}")
    opt.map(p)
    case _ =>
    println("I'm not a pf")
    opt.map(f)
  }
}

doSomething[Int, Option[Int]](Some(5))(positive) // partial function case

doSomething[Int, String](Some(5)) { // tricky case
  case s => s.toString
}

Вы можете поиграть с ним здесь:

...