微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

scala模式匹配函数 – 如何绕过类型擦除

我想模式匹配一​​个函数,问题是类型擦除.请注意,在下面的代码段中,尽管发出警告,但发生了匹配并且发生了“错误”.

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类受编译器保护,并且只在运行时解析匹配

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

总而言之,我想知道是否有一种优雅的模式匹配函数方法,也许可以理解为什么上面的例子像他们一样

解决方法

简短的回答:你必须撤消擦除,使用TypeTag来修改类型.

I don’t understand how the case class can enforce types whereas the pattern match can’t.

因为您的case类没有类型参数.只删除泛型类型,这就是它被称为“部分擦除”的原因.

相关问题: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

我不确定是否有办法编写一个提取器来使匹配块更好.

如果您需要以丢失静态类型信息的方式传递这些函数(将它们推送到函数[_,_]的集合中,然后使用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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐