Огляд TASTy

Language
This doc page is specific to Scala 3, and may cover new concepts not available in Scala 2.

Створіть файл вихідного коду Scala 3 Hello.scala:

@main def hello = println("Hello, world")

і скомпілюйте файл з scalac:

$ scalac Hello.scala

ви помітите, що серед інших отриманих файлів, scalac створює файли з розширенням .tasty:

$ ls -1
Hello$package$.class
Hello$package.class
Hello$package.tasty
Hello.scala
hello.class
hello.tasty

Виникає питання: «Що таке tasty?»

Що таке TASTy?

TASTy це акронім терміну, Типізоване абстрактне синтаксичне дерево (Typed Abstract Syntax Trees). Це високорівневий формат для Scala 3, і в цьому документі ми називатимемо його як Tasty.

Перше, що важливо знати, це те, що файли Tasty генеруються компілятором scalac, та містять всю інформацію про ваш вихідний код, включаючи синтаксичну структуру вашої програми, і повну інформацію про типи, позицію та навіть документацію. Файли Tasty містять набагато більше інформації, ніж файли .class, які створюються для роботи на JVM. (Детальніше далі.)

У Scala 3 процес компіляції виглядає так:

         +-------------+    +-------------+    +-------------+
$ scalac | Hello.scala | -> | Hello.tasty | -> | Hello.class |
         +-------------+    +-------------+    +-------------+
                ^                  ^                  ^
                |                  |                  |
           Ваш вихідний        Файл TASTy        Файл класу
               код             для scalac          для JVM
                            (містить повну       (неповна
                               інформацію)        інформація)

Ви можете переглянути вміст файлу .tasty у зрозумілій формі, запустивши на ньому компілятор із прапорцем -print-tasty. Ви також можете переглянути вміст, декомпільований у формі, подібній до вихідного коду Scala, використовуючи прапор -decompile.

$ scalac -print-tasty hello.tasty
$ scalac -decompile hello.tasty

Проблеми з файлами .class

Через проблему стирання типів, файли .class містять неповне представлення про ваш код. Простий спосіб продемонструвати це приклад з List.

Стирання типів означає, що коли ви пишете наступний код Scala:

val xs: List[Int] = List(1, 2, 3)

цей код компілюється у файл .class, який має бути сумісним із JVM. Результатом цієї вимоги сумісності код всередині цього файлу класу — який ви можете побачити за допомогою команди javap — виглядає так:

public scala.collection.immutable.List<java.lang.Object> xs();

Результат команди javap показує Java-уявлення того, що міститься у файлі класу. Зверніть увагу, що xs більше не визначений як List[Int]; він по суті представлений як List[java.lang.Object]. Щоб ваш код Scala працював із JVM, тип Int має бути стертим.

Далі, коли ви отримуєте елемент вашого List[Int] у вашому Scala коді, наприклад:

val x = xs(0)

отриманий файл класу матиме операцію перетворення для цього рядка коду, яку ви можете уявити так:

int x = (Int) xs.get(0)               // Java-подібно
val x = xs.get(0).asInstanceOf[Int]   // більш Scala-подібно

Знову ж таки, це зроблено для сумісності, щоб ваш код Scala міг працювати на JVM. Однак, інформація про те, що ми вже мали список цілих чисел, втрачається у файлах класу. Це створює проблеми під час спроби збірки Scala програми з уже скомпільованою бібліотекою. Для цього нам потрібно більше інформації, ніж зазвичай міститься у файлах класу.

Ця дискусія охоплює лише тему стирання типу. Існують подібні проблеми для кожної іншої конструкції Scala, про які JVM не знає, включно з union, intersection, trait with parameters та багатьма іншими відмінностями Scala 3.

На допомогу приходить TASTy

Таким чином, на відміну від відсутньої інформації про вихідні типи у .class файлах або тільки публічного API (як у «Pickle» форматі Scala 2.13), формат TASTy зберігає повне абстрактне синтаксичне дерево (AST) після перевірки типів. Зберігання всього AST має багато переваг: воно дає можливість окремої компіляції, перекомпіляції для іншої версії JVM, статичного аналізу програм і багато іншого.

Ключові моменти

Отже, це перший висновок з цього розділу: типи, які ви вказуєте у своєму коді Scala, не зовсім точно представлені у файлах .class.

Другим ключовим моментом є розуміння того, що існують відмінності між інформацією, яка доступна під час компіляції та виконання:

  • Під час компіляції, scalac читає та аналізує ваш код, він знає, що xs є List[Int]
  • Коли компілятор записує ваш код у файл класу, він записує xs як List[Object], та додає інформацію про перетворення усюди, де йде звернення до xs
  • Потім під час виконання — коли ваш код працює в JVM — JVM не знає, що ваш список є List[Int]

Зі Scala 3 та Tasty, є ще одна важлива примітка про час компіляції:

  • Коли ви пишете код на Scala 3, що використовує інші Scala 3 бібліотеки, scalac більше не має читати їх .class файли; він може прочитати їх .tasty файли, які, як згадувалось, є точним представленням вашого коду. Це важливо для забезпечення окремої компіляції та сумісності між Scala 2.13 і Scala 3.

Переваги Tasty

Як ви можете зрозуміти, доступ до повного представлення вашого коду має багато переваг:

  • Компілятор використовує його для підтримки окремої компіляції.
  • Сервер мови, що базується на Мовному серверному протоколі (Language Server Protocol) використовує його для підтримки гіперпосилань, завершення команд, документації, та таких глобальних операцій як, пошук звернень та перейменування.
  • Tasty створює чудову основу для нового покоління макросів основаних на рефлексії.
  • Оптимізатори та аналізатори можуть використовувати його для глибокого аналізу коду та розширеної генерації коду.

У відповідній примітці, Scala 2.13.6 має програму для читання TASTy, а компілятор Scala 3 також може читати формат 2.13 «Pickle». У сторінці з classpath сумісності посібнику з міграції на Scala 3 пояснюється перевага можливості крос-компіляції.

Більше інформації

Підсумовуючи, Tasty — це високорівневий формат обміну для Scala 3, а файли .tasty містять повне представлення вашого вихідного коду, що надає до переваги, описані у попередніх розділах. Щоб дізнатися більше, перегляньте ці ресурси:

Ці статті містять додаткову інформацію про макроси Scala 3:

Contributors to this page: