scala模式匹配一​​个函数 - 如何绕过类型擦除 [英] scala pattern match a function - how to get around type erasure

查看:148
本文介绍了scala模式匹配一​​个函数 - 如何绕过类型擦除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 阶> def f1 =()=> true 
f1:()=>布尔

scala> val fl = f1
fl:()=>布尔=< function0>

scala>

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

scala>

scala> fl match {
| case fp:Function0 [String] => 1
|案例_ => 2
| }
< console>:11:warning:fruitless type test:type()=>的值。布尔值不能是a()=>字符串(但仍可能与其擦除相匹配)
case fp:Function0 [String] => 1
^
res9:Int = 1

scala>

我能想到的是一个包装函数的案例类。我得到类型安全,注意下面的错误。 ,这首先是不雅观的,其次,我不明白case类如何强制类型,而模式匹配不能。我唯一的猜测是case类受到编译器的保护,并且匹配只在运行时解决

 阶> case类FunctionWrapper(fn:()=>布尔)
定义的类FunctionWrapper

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

scala> def fs =()=>whatever
fs:()=>字符串

scala> val fws = FunctionWrapper(fs)
< console>:10:error:type mismatch;
found:()=>字符串
required:()=>布尔
val fws = FunctionWrapper(fs)
^

scala> fw匹配{
| case FunctionWrapper(f)=> f()
|案例_ =>假
| }
res10:Boolean = true

总结一下,我想知道是一种优雅的方式来模式匹配一​​个函数,也许理解为什么上面的例子像他们做的那样行事

解决方案

简短的回答:你必须撤消删除操作,用 TypeTag 来表示类型。


我不明白case case类如何强制类型,而模式匹配不能。


因为你的case类没有类型参数。只有泛型类型被擦除,这就是为什么它被称为 partial 擦除。



相关问题:针对不同类型的列表的通用不适用方法。以下代码基本上与其中的答案相同,但使用函数而不是列表:

  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 _ => ((i:Int)=> i + 1)
// res0:Int = 1

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

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

我不确定是否有写入提取器的方法使匹配块更好。



如果您需要以失去静态类型信息的方式传递这些函数(将它们推送到函数[_,_] ,然后用作Akka消息等),那么你也需要传递标签:

<$ p $ (a:A)(隐式的val标签:TypeTag [A])$ b $($)

case class b
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)=>!b(b:Boolean)=> if(b)2 else 0))
// res1:Int = 2

foo ))
// res2:Int = 3


I would like to pattern match a function, the problem is type erasure. Notice how in the snippet below, despite the warning issued a match occurs and a "wrong" one at that.

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>

What I could come up with is a case class wrapping up the function. I get type safety, notice the error below. But, this is, first, inelegant and second, I don't understand how the case class can enforce types whereas the pattern match can't. The only guess I would have is that the case class is protected by the compiler and that the match is only resolved against during runtime

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

To sum up, I would like to know if there is an elegant way to pattern match a function, and perhaps understand why the examples above acted as they did

解决方案

The short answer: You've got to undo the erasure be reifying the types with TypeTag.

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

Because your case class has no type parameters. Only generic types are erased, which is why it's called "partial erasure".

Related question: Generic unapply method for different types of List. The following code is essentially the same as one of the answers there, but using functions instead of lists:

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

I'm not sure whether there's a way to write an extractor to make the match block nicer.

If you need to pass these functions in a way that loses the static type information (shoving them into a collection of Function[_, _], using then as Akka messages, etc.) then you need to pass the tag around too:

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

这篇关于scala模式匹配一​​个函数 - 如何绕过类型擦除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆