2015-08-27 3 views
3

Я хотел бы, чтобы шаблон соответствовал функции, проблема заключается в стирании типа. Обратите внимание, что в приведенном ниже фрагменте, несмотря на предупреждение , выдается совпадение и «неправильный».scala pattern соответствует функции - как обойти стирание типа

scala> def f1 =()=>true 
f1:() => Boolean 

scala> val fl = f1 
fl:() => Boolean = <function0> 

scala> 

scala> fl match { 
    | case fp :Function0[Boolean] => 1 
    | case _ => 2 
    | } 
res8: Int = 1 

scala> 

scala> fl match { 
    | case fp :Function0[String] => 1 
    | case _ => 2 
    | } 
<console>:11: warning: fruitless type test: a value of type() => Boolean cannot also be a() => String (but still might match its erasure) 
       case fp :Function0[String] => 1 
        ^
res9: Int = 1 

scala> 

Что я могу придумать, это класс case, который завершает функцию. Я получаю тип безопасности, обратите внимание на ошибку ниже. Но, это, во-первых, inelegant и во-вторых, я не понимаю, как класс case может применять типы, тогда как совпадение шаблона не может. Единственное предположение, я бы в том, что класс случае защищен компилятором и что матч только разрешен против во время выполнения

scala> case class FunctionWrapper(fn:()=>Boolean) 
defined class FunctionWrapper 

scala> val fw = FunctionWrapper(fl) 
fw: FunctionWrapper = FunctionWrapper(<function0>) 

scala> def fs =()=>"whatever" 
fs:() => String 

scala> val fws = FunctionWrapper(fs) 
<console>:10: error: type mismatch; 
found :() => String 
required:() => Boolean 
     val fws = FunctionWrapper(fs) 
           ^

scala> fw match { 
    | case FunctionWrapper(f) => f() 
    | case _ => false 
    | } 
res10: Boolean = true 

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

+0

Может ли бесформенная помощь? (http://stackoverflow.com/a/37817572/6309) – VonC

ответ

4

Короткий ответ: вам нужно отменить стирание, чтобы оформить типы с помощью TypeTag.

Я не понимаю, как класс case может применять типы, тогда как совпадение шаблона не может.

Поскольку ваш класс case не имеет параметров типа.Только общие типы стираются, поэтому он называется «частичный erasure».

Вопрос, связанный с этим: Generic unapply method for different types of List. Следующий код является по существу таким же, как один из ответов нет, но используя функции вместо списков:

import scala.reflect.runtime.universe._ 

def foo[A : TypeTag](a: A): Int = typeOf[A] match { 
    case t if t =:= typeOf[Int => Int] => a.asInstanceOf[Int => Int](0) 
    case t if t =:= typeOf[Boolean => Int] => a.asInstanceOf[Boolean => Int](true) 
    case _ => 3 
} 

foo((i: Int) => i + 1) 
// res0: Int = 1 

foo((b: Boolean) => if (b) 2 else 0) 
// res1: Int = 2 

foo((b: Boolean) => !b) 
// res2: Int = 3 

Я не уверен, есть ли способ, чтобы написать экстрактор, чтобы сделать матч блок лучше.

Если вам нужно передать эти функции таким образом, что теряет информацию статического типа (толкая их в коллекцию Function[_, _], используя то, как сообщения Akka и т.д.), то вам нужно передать тег вокруг слишком:

import scala.reflect.runtime.universe._ 

case class Tagged[A](a: A)(implicit val tag: TypeTag[A]) 

def foo[A, B](tagged: Tagged[A => B]): Int = tagged.tag.tpe match { 
    case t if t =:= typeOf[Int => Int] => tagged.a.asInstanceOf[Int => Int](0) 
    case t if t =:= typeOf[Boolean => Int] => tagged.a.asInstanceOf[Boolean => Int](true) 
    case _ => 3 
} 
foo(Tagged((i: Int) => i + 1)) 
// res0: Int = 1 

foo(Tagged((b: Boolean) => if (b) 2 else 0)) 
// res1: Int = 2 

foo(Tagged((b: Boolean) => !b)) 
// res2: Int = 3 
+0

Проблема с этим способом использования 'TypeTag' заключается в том, что вы должны сохранять точный тип все время. Скажите, что у вас есть список функций, хранящихся в списке, и весь компилятор не знает, что такое точные типы функций, и вы вызываете foo на них, не будете делать то, что вы ожидаете. Таким образом, вы по-прежнему сильно ограничены в отношении соответствия шаблонов, по сравнению с тем, что вы могли бы сделать, если стирание не попало в изображение. Если вы упаковываете 'TypeTag' с самой функцией (внутри обертки), тогда вы можете различать фактический тип в своих шаблонах. –

+0

Обновлено, чтобы продемонстрировать использование обертки. –

3

предупреждение здесь на самом деле два раза:

1) Во-первых, «значение типа() => Boolean не может быть также() => String ": действительно, вы сопоставляетесь с () => Boolean, и он никогда не может быть одновременно () => String, поэтому случай не имеет смысла, и в идеальном мире никогда не должно совпадать. Однако стирание в игру вступает во второй части намеков на

2) «(но все еще может соответствовать его стиранию)»: стирание здесь означает, что экземпляры () => Boolean (ака Function0[Boolean]) и экземпляры () => String (ака Function0[String]) представлены именно то же самое во время выполнения. Таким образом, нет никакого способа их отличить, и когда вы сопоставляете шаблон с Function0[String], на самом деле компилятор может только сообщить, что это Function0, но не знает, Function0[Boolean] или Function0[String].

Сразу же вторая часть предупреждения легко пропустить здесь. бы fl наберется Any, первая частью предупреждения не будет применяться, и вы бы получили более полезное сообщение:

scala> (fl:Any) match { 
    | case fp :Function0[Boolean] => 1 
    | case _ => 2 
    | } 
<console>:11: warning: non-variable type argument Boolean in type pattern() => Boolean is unchecked since it is eliminated by erasure 
      case fp :Function0[Boolean] => 1 

Что касается решения, то вы мало что могут сделать, кроме действительно оберточную функцию пример. К счастью, вам не нужно писать одну конкретную оболочку для каждого возможного типа возврата. Scala предоставляет ClassTag и TypeTag, чтобы обойти стирание, и мы можем воспользоваться им, сохранив это в (общей) оболочке функции. Тем не менее, это будет довольно громоздким в использовании и ошибочным на стороне небезопасности, поскольку вам придется сопоставлять с ClassTag/TypeTag, хранящимся внутри обертки, и лить (либо напрямую через asInstanceOf, либо косвенно через одно и то же сопоставление шаблонов) функцию к соответствующему типу функции.

+0

Спасибо, это помогает немного разъяснить, но вы все еще не ставите решение или, наоборот, говорите, что его нет ... – Yaneeve

+0

Я расширил мой ответ. –

Смежные вопросы