17

マクロでプライベート コンストラクターを使用したい。この例は正の整数ですが、基本パターンは偶数などの他の数値型だけでなく、電子メール アドレスやディレクトリ名などの文字列から派生した型にも使用できます。コンストラクターを非公開にすることにより、ユーザーは不正な型を作成する機会を拒否されます。次のコードがあります。

object PosInt
{
  import language.experimental.macros 
  import reflect.runtime.universe._
  import reflect.macros.Context

  def op(inp: Int): Option[PosInt] = if (inp > 0) Some(new PosInt(inp)) else None

  def apply(param: Int): PosInt = macro apply_impl

  def apply_impl(c: Context)(param: c.Expr[Int]): c.Expr[PosInt] =
  {
    import c.universe._
    param match {
      case Expr(Literal(i)) if (i.value.asInstanceOf[Int] > 0) =>
      case Expr(Literal(i)) if (i.value.asInstanceOf[Int] == 0) => c.abort(c.enclosingPosition, "0 is not a positive integer") 
      case Expr(Literal(i)) => c.abort(c.enclosingPosition, "is not a positive integer")      
      case _ => c.abort(c.enclosingPosition, "Not a Literal")
    }    
    reify{new PosInt(param.splice)}    
  }  
}

class PosInt (val value: Int) extends AnyVal

ただし、PosInt コンストラクターをプライベートにすると、マクロは期待どおりにコンパイルされますが、マクロを使用しようとするとエラーが発生します。式ツリーを手動で構築する方法を見つけることはできませんが、とにかくそれが役立つかどうかはわかりません. とにかく私はこれを行うことができますか?

PosInt が値クラスでない場合でも、プライベート コンストラクターを使用することはできません。値クラスを使用しない回答を受け入れます。値クラスの欠点は、型が消去されることです。さらに、2次元座標のサブセットのように私が興味を持っているクラスは、とにかく値クラスとして実装できません。私は実際には正の整数には興味がありません。単純なテストベッドとして使用しているだけです。Scala 2.11M5 を使用しています。Scala 2.11 には quasiquotes 機能が追加されます。quasiquotes の使用方法についてはまだ考えていません。現時点でのすべての資料は、私が持っていない Macro Paradise に精通していることを前提としているようです。

4

2 に答える 2

2

残念なことに、あなたが達成しようとしていることに対して、マクロはこのようには機能しません。コンパイル時に AST を操作するだけです。最終結果がどうであれ、それは常に(マクロなしで) Scala で文字どおりに記述できるものです。

したがって、 の可能な値を制限するにPosIntは、パブリック コンストラクターまたはコンパニオン オブジェクトのファクトリ メソッドのいずれかで、実行時チェックが必要になります。

実行時例外が気に入らない場合、考えられるアプローチの 1 つは次のとおりです。

  • クラスでコンストラクターをプライベートにします。
  • (たとえば)createコンパニオン オブジェクトにメソッドを提供しますOption[PosInt](またはTry[PosInt]、引数が範囲外の場合に「失敗」を表現できるようにする任意の他のタイプ)。
  • コンパイル時に引数が範囲内にあることapplyを確認し、単に.create(x).get

この場合、 Option の呼び出し.getは許容されますNone

欠点は、チェックを 2 回繰り返す必要があることです。コンパイル時に 1 回、実行時に 1 回です。

于 2016-08-12T13:06:48.027 に答える