2015-11-04 11 views
5

は、一般的な機能を検討し、コンパイル時に非ジェネリック型を強制するには?はどのよう


私が解決したい下っ端の問題は、このようなものです:def addCase[T: ClassTag](...、まだClassTagaddCase関数は型消去のようClassTagを必要とすることによって解決することができ、警告、につながる

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[List[Int]](_ => {println("Int"); true}) 
addCase[List[String]](_ => {println("String"); false}) 

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

上記のコードは、と表示されません。警告やエラーは一切出されていませんが、何も出ていませんか?

+3

あなたはここでakkaを扱っているので、私は「一般的な」メッセージをしようとすると大きな苦痛を感じるでしょう、特にシリアライゼーションの一種が進行中です。より一般的なレベルでは、IntとList [Int]の両方の型は両方とも*であるため、他の制限のない単一の型パラメータはそれらを区別しません。おそらく、標準的なtypeclassの解決策で十分でしょう。あるいは、dk14から提供されているようなより高度なものを使うこともできます。 – melps

+1

あなたは 'List'から保護を加えることができます:http://stackoverflow.com/questions/15962743/using-context-bounds-negatively-to-ensure-type-class-instance-is-absent-from-s – dk14

+0

しかし私はどのように多形型に遺伝的に生成するのかわからない – dk14

答えて

5

これを反映せずにタイプシステムにそのまま適用する方法はありません。

これを実行する最も良い方法は、実行時にタイプパラメータが消去されないという証拠を提供するNonEraseable[A]などのタイプクラスを持つことです。範囲内の暗黙のNonEraseable[A]は、Aに型パラメータがないことを意味するはずです。これらは手動で作成する退屈なように、暗黙のマクロは仕事をすることができます見て:

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はあなたが望む種類の種類です。

3

あなたは、少なくとも次のように、それは実行時に失敗してもらうことができます:

def addCase[T: ClassTag](handler: T => Boolean): Unit = 
    if (classTag[T].runtimeClass.getTypeParameters.nonEmpty) { 
    // throw an exception 
    } else { 
    // the main code 
    } 

コンパイル時の障害はmacroの代わりに、(未テスト概算)機能を使用して達成することができます。

def addCase[T](handler: T => Boolean): Unit = macro addCaseImpl 

def addCaseImpl[T: c.WeakTypeTag](c: Context)(handler: c.Expr[T => Boolean]): c.Expr[Unit] = 
    if (c.weakTypeOf[T].typeParams.nonEmpty) { 
    c.abort(c.enclosingPosition, "Generic types not allowed in addCase") 
    } else { 
    // generate code for main line 
    } 
+0

"+1"ですが、これはあまりにもハードコアなマクロではなく、同じモジュールでテストすることさえできません。たぶんいくつかの形のないソリューションがあります。 P.S.私はいくつかのエビデンスに基づいて試してみましたが、スカラがジェネリックとハイオーダーをサポートするのは非常に奇妙なので動作しませんでした。何らかの理由でそのようなことが許されます: 'implicit def notSimple [T、M [_]](t:M [T])=" "; notSimple(9) '戻り型を' M [T] 'に変更すると、コンパイラが強制終了しました。 ***** 9はどのようにM [T]ですか? :) – dk14