2015-09-19 8 views
5

==以上のオーバーヘッドを持つScala用の型安全な===実装がありますか?つまり、ScalazとScalaUtilsの===とは異なり、ストレートマクロを使用してチェックを実行する実装ですか?型セーフはマクロに等しいか?

私は多くの場所で===を使用したいと思いますが、これらはホットスポットなので、余分なランタイムコスト(タイプクラスなどの構築など)が発生しないようにします。

+1

暗黙的なバリュークラスに対して、マクロ実装があなたを買うのはどうでしょうか? –

+0

@TravisBrownの値クラスは割り振りコストを節約しますが、間違っていなければ間接費を節約しません。これは関連するかもしれません:http://typelevel.org/blog/2015/08/06/machinist.html –

答えて

1

machinistで簡単に達成できると思います。あなたがいるなら、あなたは==


上ゼロオーバーヘッド(余分な配分、余分な間接)で===を使用することができます

import scala.{specialized => sp} 

import machinist.DefaultOps 

trait Eq[@sp A] { 
    def eqv(lhs: A, rhs: A): Boolean 
} 

object Eq { 
    implicit val intEq = new Eq[Int] { 
    def eqv(lhs: Int, rhs: Int): Boolean = lhs == rhs 
    } 

    implicit class EqOps[A](x: A)(implicit ev: Eq[A]) { 
    def ===(rhs: A): Boolean = macro DefaultOps.binop[A, Boolean] 
    } 
} 

GitHubの上のREADMEは正確に===例を示しますすぐに使える実装を探しているのはspire(そこから機械工が生まれたもの)が提供しています。

また、catsがあります。

これらはどちらもマクロベースで、実装にはmachinistが使用されています。

+0

尖塔と猫の実装はどちらも型クラスインスタンスを必要とします。だから、各使用時にそれらをインスタンス化しないでください。 –

+0

@TravisBrown、わかりません。あなたが 'EqOps'から' ev'を使わない限り、型クラスは等価操作の可用性の証明としてのみ使われるので、あなたはうまくいくはずです。私が間違っている? –

0

マシニストに基づく回答がおそらく最も良いでしょう。

import scala.collection.breakOut 
import scala.language.experimental.macros 
import scala.reflect.macros.blackbox 

object Implicits { 
    implicit class TripleEquals[A](a: A) { 
    def === [B >: A](b: B): Boolean = macro Macros.equalsImpl[A, B] 
    } 
} 

object Macros { 
    private val positiveList = Set("scala.Boolean", "scala.Int", "scala.Long", 
           "scala.Float", "scala.Double", "scala.Option) 
    private val negativeList = Set("java.lang.Object", "java.io.Serializable", 
           "<refinement>") 

    def equalsImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: blackbox.Context) 
                (b: c.Expr[A]): c.Tree = { 
    import c.universe._ 
    val bTpe = weakTypeOf[B] 
    val base = bTpe.baseClasses 
    val names: Set[String] = base.collect { 
     case sym if sym.isClass => sym.fullName 
    } (breakOut) 

    // if a primitive is inferred, we're good. otherwise: 
    if (names.intersect(positiveList).isEmpty) { 
     // exclude all such as scala.Product, scala.Equals 
     val withoutTopLevel = names.filterNot { n => 
     val i = n.lastIndexOf('.') 
     i == 5 && n.startsWith("scala") 
     } 
     // exclude refinements and known Java types 
     val excl = withoutTopLevel.diff(negativeList) 
     if (excl.isEmpty) { 
     c.abort(c.enclosingPosition, s"Inferred type is too generic: `$bTpe`") 
     } 
    } 

    // now simply rewrite as `a == b` 
    val q"$_($a)" = c.prefix.tree 
    q"$a == $b" 
    } 
} 

これはタプルので、高いkindedタイプの動作、まだない:ここで、このようなAny又はAnyRefまたは2つの無関係な場合クラス(Product with Serializable)の典型的な混合を推測ような場合を検出するよりハック変異体であります残念ながらSome(1) === Some("hello")がコンパイルされていますが、故意に失敗しています。


編集:高kindedタイプをサポートするために、この上に構築改善a small library

関連する問題