【问题标题】:Checking if a partial function in scala is definied for a value with unknow type检查是否为具有未知类型的值定义了scala中的部分函数
【发布时间】:2013-03-05 02:55:40
【问题描述】:

我具有以下特征(获得 2 级多态性click

type Id[A] = A
trait ~>[F[_], G[_]] {
def apply[A](a: F[A]): G[A]
def isDefinedAt[A](a: A): Boolean}

还有一个将偏函数转换为此特征的函数:

implicit def pft[B: ClassTag](f: PartialFunction[B, B])= new (Id ~> Id) {
def apply[A](a: A): A = f(a.asInstanceOf[B]).asInstanceOf[A]
def isDefinedAt[A: ClassTag](a: A)(implicit ev2: ClassTag[A]) : Boolean = /*type check*/
               f.isDefinedAt(a.asInstanceOf[B]) }

所以我的问题是 isDefinedAt 方法。我必须在运行时检查 A 是否是 B 的实例。 由于类型擦除,a.isInstanceOf[B] 不起作用。

我尝试使用 TypeTag/ClassTag 和 B 效果很好,但 A 的类型总是 Any。

那么,如何检查 A 是否是 B 的实例?

更新:我在这段代码中使用它:

  def map[A](f: Id ~> Id, x: A): A =
    {
      val y = x match {
        //many matches more on my own data structures
        case l : List[_] => l.map(map(f,_))
        case m : Map[_,_] => m.map(map(f,_))
        case (a,b) => (map(f,a),map(f,b))
        case a => a
      }
      if (f.isDefinedAt(y))
        f(y).asInstanceOf[A]
      else
        y.asInstanceOf[A]
    }

如果我直接使用 ~>,那么 typeTag[A].tpe <: typetag>

但如果我将它与这个映射函数一起使用,typeTag[A].tpe 始终是 Any。

【问题讨论】:

    标签: scala type-erasure higher-rank-types partialfunction


    【解决方案1】:

    如果您可以修改 isDefinedAt 的签名以采用 TypeTag,您可以这样实现 pft:

      type Id[A] = A
      trait ~>[F[_], G[_]] {
        def apply[A](a: F[A]): G[A]
        def isDefinedAt[A: TypeTag](a: A): Boolean
      }
    
      implicit def pft[B: TypeTag](f: PartialFunction[B, B]) = new (Id ~> Id) {
        def apply[A](a: A): A = f(a.asInstanceOf[B]).asInstanceOf[A]
        def isDefinedAt[A: TypeTag](a: A): Boolean =
          typeTag[A].tpe =:= typeTag[B].tpe && f.isDefinedAt(a.asInstanceOf[B])
      }
    

    此解决方案为BA 获取TypeTag,并在委托给部分函数的isDefinedAt 方法之前验证它们是否为同一类型。有关类型标签的更多信息,请参阅this answer

    例如:

    val halfEven: PartialFunction[Int, Int] = {
      case n if n % 2 == 0 => n / 2
    }
    
    val halfEvenT: Id ~> Id = halfEven
    
    halfEvenT.isDefinedAt(1) // false
    halfEvenT.isDefinedAt(2) // true
    halfEvenT.isDefinedAt("test") // false
    

    更一般地说,您可以将受约束的部分转换定义为采用约束A 的附加更高种类的类型:

      trait ConstrainedPartialTransformation[F[_], G[_], C[_]] {
        def apply[A: C](a: F[A]): G[A]
        def isDefinedAt[A: C](a: A): Boolean
      }
    
      implicit def cpft[B: TypeTag](f: PartialFunction[B, B]) = new ConstrainedPartialTransformation[Id, Id, TypeTag] {
        def apply[A: TypeTag](a: A) = f(a.asInstanceOf[B]).asInstanceOf[A]
        def isDefinedAt[A: TypeTag](a: A) =
          typeTag[A].tpe =:= typeTag[B].tpe && f.isDefinedAt(a.asInstanceOf[B])
      }
    

    这类似于 Scalaz 7 如何支持在 A 上绑定上下文的自然转换。请参阅 Scalaz 7 中的 ConstrainedNaturalTransformation

    【讨论】:

    • 感谢您的回答!我现在明白了,我把问题说得很简单。我的问题还在于我如何使用这种转换。我尝试了与您的第一个解决方案基本相同的方法,并使用我的代码得到了 typeTag[A].tpe = Any。我会尽快更新我的问题。我现在可能不知道如何使用您的第二个解决方案(Int 没有可用的 TypeTag)
    • 啊,我明白了。推断 Any 的原因是编译器在 map 函数的主体中没有任何关于 List 或 Map 的类型参数的信息。
    • 考虑偏函数:val pf: PartialFunction[List[Int], List[Int]] = { case l =&gt; l.reverse }map 应该如何处理这个例子?看起来 map 函数假设传递给它的 ~&gt; 是为不带参数的类型定义的。我可以让map 在这个假设下工作,但我想知道这个问题是否应该以不同的方式解决?也许重述你正在尝试做的事情?
    • 我正在尝试为 AST 编写通用映射函数。示例:将一个 AST 转换为另一个重命名所有标识符的 AST。有点像 Kiama 中的术语重写,遗憾的是我无法使用。
    • 感谢您对 List 或 Map 的类型参数的提示,我得到了它的工作,因为我知道我在每一点上都使用哪种 Map 或 List :)。我还添加了 map[A: TypeTag](...),并测试是否在 x 而不是 y 上定义了 f。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    • 2015-12-29
    • 2023-02-16
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多