Как работает Scala (2.8) Manifest? - PullRequest
       26

Как работает Scala (2.8) Manifest?

23 голосов
/ 27 августа 2010

У меня есть некоторый код Scala, который довольно интенсивно использует дженерики, и я почерпнул из документов, что использование манифеста в ограничениях параметризации может помочь мне обойти проблемы стирания типов (например, я хочу создать новый объект универсальный тип). Только я хотел бы больше узнать о том, как это работает. Это почти похоже на что-то вроде хэш-карты, которая получает запись для каждого сайта вызова ... Может кто-нибудь здесь уточнить?

class Image[T <: Pixel[T] : Manifest](fun() => T, size: Array[Int], data: Array[T]) {
    def this(fun: () => T, size: Array[T]) {
        this(fun, size, new Array[T](size(0) * size(1));
    }
}

Это то, что, по-видимому, не отражено ни в одной документации, которую я нашел на сайте, и в Google я в основном получаю старые посты, которые имеют совершенно другой синтаксис, и, поскольку в 2.8, похоже, есть много вещей изменилось, я не уверен, что они все еще точны.

Ответы [ 2 ]

35 голосов
/ 28 августа 2010

Прошло некоторое время с тех пор, как я копался в исходном коде Scala в поисках ответа на тот же вопрос ... но короткий ответ, насколько я помню, -

Манифест - это чит-код, позволяющий компиляторуобойти стирание типа (оно не используется во время выполнения).Это приводит к тому, что во время компиляции генерируются несколько путей кода для возможных типов ввода, соответствующих манифесту.

Манифест разрешается неявно, но если во время компиляции существует неопределенность относительно типа манифеста, то компиляторОстановится.

С копией Manifest у вас есть несколько доступных вещей.Главное, что вы обычно хотите - это либо java.lang.Class, который был удален с помощью erasure:

class BoundedManifest[T <: Any : Manifest](value: T) {
  val m = manifest[T]
  m.erasure.toString match {
    case "class java.lang.String" => println("String")
    case "double" | "int"  => println("Numeric value.")
    case x => println("WTF is a '%s'?".format(x))
    }
}

class ImplicitManifest[T <: Any](value: T)(implicit m: Manifest[T]) {
  m.erasure.toString match {
    case "class java.lang.String" => println("String")
    case "double" | "int" => println("Numeric value.")
    case x => println("WTF is a '%s'?".format(x))
  }
}

new BoundedManifest("Foo Bar!")
// String 
new BoundedManifest(5)
// Numeric value.
new BoundedManifest(5.2)
// Numeric value.
new BoundedManifest(BigDecimal("8.62234525"))
// WTF is a 'class scala.math.BigDecimal'?
new ImplicitManifest("Foo Bar!")
// String 
new ImplicitManifest(5)
// Numeric value.
new ImplicitManifest(5.2)
// Numeric value.
new ImplicitManifest(BigDecimal("8.62234525"))
// WTF is a 'class scala.math.BigDecimal'?

Это довольно странный пример, но показывает, что происходит.Я запустил это и для вывода FWIW в Scala 2.8.

Граница [T ... : Manifest] является новой в Scala 2.8 ... вы привыкли неявно захватывать манифест, как показано в ImplicitManifest.Вы на самом деле не получаете копию манифеста.Но вы можете извлечь один из них внутри своего кода, сказав, что val m = manifest[T] ... manifest[_] определен в Predef и, очевидно, найдет правильный тип манифеста внутри ограниченного блока.

Два других основных элемента, которые выget from Manifest - это <:< и >:>, которые проверяют подтип / супертип одного манифеста против другого.Если я правильно помню, это ОЧЕНЬ наивная мудрая реализация и не всегда совпадающая, но у меня есть куча производственного кода, использующего их для проверки нескольких возможных стертых входных данных.Простой пример проверки другого манифеста:

class BoundedManifestCheck[T <: Any : Manifest](value: T) {
  val m = manifest[T]
  if (m <:< manifest[AnyVal]) {
    println("AnyVal (primitive)")
  } else if (m <:< manifest[AnyRef]) {
    println("AnyRef")
  } else {
    println("Not sure what the base type of manifest '%s' is.".format(m.erasure))
  }
}


new BoundedManifestCheck("Foo Bar!")
// AnyRef
new BoundedManifestCheck(5)
// AnyVal (primitive)
new BoundedManifestCheck(5.2)    
// AnyVal (primitive)
new BoundedManifestCheck(BigDecimal("8.62234525"))
// AnyRef

У Хорхе Ортиса есть отличный пост в блоге (хотя и старый) по этому поводу: http://www.scala -blogs.org / 2008/10 / manifest-reified-types.html

EDIT :

В действительности вы можете увидеть, что делает Scala, попросив распечатать результаты фазы компилятора стирания.

Выполнение, в моем последнем примере выше scala -Xprint:erasure test.scala дает следующий результат:

final class Main extends java.lang.Object with ScalaObject {
  def this(): object Main = {
    Main.super.this();
    ()
  };
  def main(argv: Array[java.lang.String]): Unit = {
    val args: Array[java.lang.String] = argv;
    {
      final class $anon extends java.lang.Object {
        def this(): anonymous class $anon = {
          $anon.super.this();
          ()
        };
        class BoundedManifestCheck extends java.lang.Object with ScalaObject {
          <paramaccessor> private[this] val value: java.lang.Object = _;
          implicit <paramaccessor> private[this] val evidence$1: scala.reflect.Manifest = _;
          def this($outer: anonymous class $anon, value: java.lang.Object, evidence$1: scala.reflect.Manifest): BoundedManifestCheck = {
            BoundedManifestCheck.super.this();
            ()
          };
          private[this] val m: scala.reflect.Manifest = scala.this.Predef.manifest(BoundedManifestCheck.this.evidence$1);
          <stable> <accessor> def m(): scala.reflect.Manifest = BoundedManifestCheck.this.m;
          if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.AnyVal())))
            scala.this.Predef.println("AnyVal (primitive)")
          else
            if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.Object())))
              scala.this.Predef.println("AnyRef")
            else
              scala.this.Predef.println(scala.this.Predef.augmentString("Not sure what the base type of manifest '%s' is.").format(scala.this.Predef.genericWrapArray(Array[java.lang.Object]{BoundedManifestCheck.this.m().erasure()})));
          protected <synthetic> <paramaccessor> val $outer: anonymous class $anon = _;
          <synthetic> <stable> def Main$$anon$BoundedManifestCheck$$$outer(): anonymous class $anon = BoundedManifestCheck.this.$outer
        };
        new BoundedManifestCheck($anon.this, "Foo Bar!", reflect.this.Manifest.classType(classOf[java.lang.String]));
        new BoundedManifestCheck($anon.this, scala.Int.box(5), reflect.this.Manifest.Int());
        new BoundedManifestCheck($anon.this, scala.Double.box(5.2), reflect.this.Manifest.Double());
        new BoundedManifestCheck($anon.this, scala.package.BigDecimal().apply("8.62234525"), reflect.this.Manifest.classType(classOf[scala.math.BigDecimal]))
      };
      {
        new anonymous class $anon();
        ()
      }
    }
  }
}
4 голосов
/ 28 августа 2010

«Связанный с контекстом» T ... : Manifest является синтаксическим сахаром для неявного аргумента: (implicit man: Manifest[T]). Таким образом, в момент создания конструктора типа, указанного в class Image, компилятор находит / предоставляет Манифест для фактического типа, используемого для параметра типа T, и это значение «привязывается» к полученному экземпляру класса на протяжении всего его существования «привязывает» каждый конкретный экземпляр Image[Something] к Манифесту для его T.

...