Подтвердить что ты не робот

Использование частного конструктора в макросе

Я хочу использовать частный макрокоманд. Этот пример представляет собой положительное целое число, но базовый шаблон может использоваться не только для других числовых типов, таких как четные числа, но также и для производных типов, таких как адреса электронной почты или имя каталога. При закрытии конструктора пользователю запрещается использовать нелегальный тип. У меня есть следующий код:

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 Constructor закрытым, хотя макрос компилируется, как ожидается, я получаю сообщение об ошибке, если пытаюсь использовать макрос. Я не могу решить, как построить дерево выражений вручную, но я не уверен, что это все равно поможет. В любом случае я могу это сделать?

Вы все еще не можете использовать частный конструктор, даже если PosInt не является классом значений. Я приму ответ, который не использует класс значений. Недостатком классов значений является то, что они получают стирание типа. Плюс классы, которые меня интересуют как подмножества координат 2d, в любом случае не могут быть реализованы как классы значений. Меня действительно не интересуют позитивные целые числа, я просто использую их как простую тестовую кровать. Я использую Scala 2.11M5. Scala 2.11 будет иметь добавление функции квазиквадратов. Я еще не разработал, как использовать, квазикокки, поскольку все материалы в данный момент на них, похоже, знакомятся с Macro Paradise, чего у меня нет.

4b9b3361

Ответ 1

К сожалению, для того, чего вы пытаетесь достичь, макросы не работают таким образом. Они просто манипулируют АСТ во время компиляции. Независимо от конечного результата, это всегда то, что вы могли бы написать буквально в Scala (без макроса).

Таким образом, чтобы ограничить возможные значения PosInt, вам понадобится проверка времени выполнения где-либо, либо в публичном конструкторе, либо в методе factory на сопутствующем объекте.

Если исключения выполнения не подходят для вас, тогда один из возможных вариантов:

  • Сделать конструктор закрытым для класса.
  • Предоставьте (например) метод create для объекта-компаньона, который возвращает Option[PosInt] (или Try[PosInt], или какой-либо другой тип вашего выбора, который позволяет вам выражать "сбой", когда аргумент отсутствует диапазон).
  • Предоставьте метод apply для объекта-компаньона, аналогичный вашему примеру, который проверяет во время компиляции, что аргумент находится в диапазоне, а затем возвращает дерево выражений, которое просто вызывает create(x).get.

Вызов .get в опции допустим в этом случае, потому что вы уверены, что он никогда не будет None.

Недостатком является то, что вы должны повторить проверку дважды: один раз во время компиляции и один раз во время выполнения.

Ответ 2

Я не эксперт, но я решил, что дам ему шанс... В Java объем частного конструктора ограничен одним и тем же классом... поэтому объект PosInt необходимо будет перемещать в область того же класса, из которого он вызывается. С учетом сказанного я нашел статью, в которой показаны два способа удержания объекта от унаследованного @http://www.developer.com/java/other/article.php/3109251/Stopping-Your-Class-from-Being-Inherited-in-Java-the-Official-Way-and-the-Unofficial-Way.htm

В описании класса используется ключевое слово final, чтобы предотвратить его наследование. Это "официальный" способ. "Неофициальный" способ состоит в том, чтобы сделать конструктор закрытым, но добавить открытый статический метод, который возвращает объект класса...

Да, я знаю, это старый вопрос... но он остался без ответа. Вы никогда не знаете, когда старый вопрос будет лучшим ударом в результатах поиска...