如何在编译时强制执行非泛型类型 [英] How to enforce non-generic type at compile time

查看:88
本文介绍了如何在编译时强制执行非泛型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个通用函数:

def genericFn[T](fn: T => Boolean): Unit = {
  // do something involves T
}

是否可以将T(在编译时)限制为简单类型,而不是像List[Int]这样的类型?

is it possibile to restrict T (at compile time) to be a simple type, not a type like List[Int]?

我要解决的潜在问题是这样的:

the underling problem I want to solve is something like this:

var actorReceive: Receive = PartialFunction.empty
def addCase[T](handler: T => Boolean): Unit = {
    actorReceive = actorReceive orElse ({
        case msg: T => // call handle at some point, plus some other logic
            handler(msg)
    })
}

addCase函数将导致类型擦除警告,可以通过要求ClassTag来解决该警告,例如:def addCase[T: ClassTag](...,但是ClassTag仍然无法防范诸如以下的调用:

the addCase function would result in type erasure warning, which could be solved by requiring ClassTag like: def addCase[T: ClassTag](..., but ClassTag still can't guard against calls like:

addCase[List[Int]](_ => {println("Int"); true})
addCase[List[String]](_ => {println("String"); false})

actorReceive(List("str"))    // will print "Int"

以上代码将在不发出任何警告或错误的情况下打印"Int",是否有解决办法?

the above code will print "Int" while not issuing any warning or error at all, is there any way out?

推荐答案

在没有反射的情况下,无法在类型系统中按原样实施此方法.

There is no way to enforce this in the type system as-is, without reflection.

执行此操作的最佳方法是使用类型类,例如NonEraseable[A],它提供了证据,表明类型没有类型参数会在运行时删除.范围中的隐式NonEraseable[A]意味着A没有类型参数.鉴于手动创建这些代码很繁琐,因此隐式宏可以完成这项工作:

The nicest way to do this would be to have a type-class such as NonEraseable[A], that provides evidence that a type has no type parameters that would be erased at runtime. An implicit NonEraseable[A] in scope should mean that A has no type parameters. Seeing as these would be tedious to manually create, an implicit macro can do the job:

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

trait NonEraseable[A]

object NonEraseable {

    implicit def ev[A]: NonEraseable[A] = macro evImpl[A]

    def evImpl[A](c: Context)(implicit tt: c.WeakTypeTag[A]): c.Expr[NonEraseable[A]] = {
        import c.universe._
        val tpe = weakTypeOf[A]
        if(tpe.dealias.typeArgs.isEmpty)
            c.Expr[NonEraseable[A]](q"new NonEraseable[$tpe] {}")
        else
            c.abort(c.enclosingPosition, s"$tpe contains parameters that will be erased at runtime.")
    }

}

用例:

def onlySimple[A : NonEraseable](value: A): Unit = println(value)

scala> onlySimple(1)
1

scala> onlySimple(List(1, 2, 3))
<console>:13: error: List[Int] contains parameters that will be erased at runtime.
       onlySimple(List(1, 2, 3))
                 ^

使用此方法,可以在编译时强制 ,使带有上下文绑定NonEraseable的类型参数A是您想要的类型. (假设您没有作弊并手动创建类型类的实例)

Using this, you can enforce at compile time that a type parameter A with a context bound NonEraseable is the kind of type you want. (Assuming you don't cheat and manually create instance of the type class)

这篇关于如何在编译时强制执行非泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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