19
1 / 19 Scala magic Александр Подхалюзин 19 мая 2012 г.

Александр Подхалюзин, «Магия Scala изнутри»

Embed Size (px)

DESCRIPTION

В своем докладе Александр расскажет о байткоде и о том, как сохранить его совместимость и повысить производительность.

Citation preview

Page 1: Александр Подхалюзин, «Магия Scala изнутри»

1 / 19

Scala magic

Александр Подхалюзин

19 мая 2012 г.

Page 2: Александр Подхалюзин, «Магия Scala изнутри»

Syntactic sugar

Syntactic sugarForcomprehensionsBy-nameparameters

Non local return

Local functions

Lazy values

GeneratedByteCode

Binarycompatibility

Questions?

2 / 19

Page 3: Александр Подхалюзин, «Магия Scala изнутри»

For comprehensions

Syntactic sugarForcomprehensionsBy-nameparameters

Non local return

Local functions

Lazy values

GeneratedByteCode

Binarycompatibility

Questions?

3 / 19

В Scala for является синтаксическим сахаром, тем неменее правила преобразования довольно сложны

✔ Один генератор с ’yield’ тоже, что вызов метода ’map’✔ Один генератор без ’yield’ тоже, что ’foreach’✔ Несколько генераторов транслируются во ’flatMap’

f o r ( i <− 1 to 5)y i e l d i * i

f o r ( i <− 1 to 5)println ( i )

f o r {i <− 1 to 5j <− 1 to 5}

y i e l d i + j

(1 to 5) . map { i => i * i }(1 to 5) . foreach {i => println ( i )

}(1 to 5) . flatMap {i => (1 to 5) . map { j => i + j }

}

Подробнее можно почитать в спецификации.

Page 4: Александр Подхалюзин, «Магия Scala изнутри»

By-name parameters

Syntactic sugarForcomprehensionsBy-nameparameters

Non local return

Local functions

Lazy values

GeneratedByteCode

Binarycompatibility

Questions?

4 / 19

Возможность в вызов метода передавать выражениялениво это другой пример того, где неявно могут бытьзашиты closures. Следующие два примера идентичны:

de f foo ( x : => Int ) {println ( x )

}

foo (1 )

de f foo ( x : ( ) => Int ) {println ( x ( ) )

}

foo ( ( ) => 1)

Что изредка может приводить к ошибке (параметр будетвычисляться каждый раз, когда к нему будетпроизводится обращение).

Page 5: Александр Подхалюзин, «Магия Scala изнутри»

Non local return

Syntactic sugarForcomprehensionsBy-nameparameters

Non local return

Local functions

Lazy values

GeneratedByteCode

Binarycompatibility

Questions?

5 / 19

Return statement внутри closure на деле компилируется вкод, который бросает NonLocalReturnControl. Вбольшинстве случаев это удобно, но нужно учитыватьследующее

✔ В критичных по производительности кусках кода этотexception следует избегать

✔ Нельзя ловить Throwable в том месте, где этотexception бросается

✔ В силу синтаксического сахара описанного ранее, всеэто может происходить неявно (for-comprehensions иby-name parameters)

Page 6: Александр Подхалюзин, «Магия Scala изнутри»

Local functions

Syntactic sugarForcomprehensionsBy-nameparameters

Non local return

Local functions

Lazy values

GeneratedByteCode

Binarycompatibility

Questions?

6 / 19

В Java любой вспомогательный код оформляется в видеprivate методов на уровне класса. В Scala это можносделать более структурированным, если использоватьлокальные методы, хотя на уровне bytecode получаетсяпримерно тоже самое:

c l a s s L o c a l F u n c t i o n s {de f foo {va r i = 1v a l j = 2de f local {i += j

}locallocal

}}

impor t scala . runtime . IntRef ;p u b l i c c l a s s L o c a l F u n c t i o n s {

p u b l i c vo i d foo ( ) {IntRef i$1 = new IntRef (1 ) ;i n t j$1 = 2 ;local$1 ( i$1 , j$1 ) ;local$1 ( i$1 , j$1 ) ;

}p r i v a t e f i n a l v o i d local$1 (IntRef intref , i n t i ) {intref . elem += i ;

}p u b l i c L o c a l F u n c t i o n s ( ) {}

}

Page 7: Александр Подхалюзин, «Магия Scala изнутри»

Lazy values

Syntactic sugarForcomprehensionsBy-nameparameters

Non local return

Local functions

Lazy values

GeneratedByteCode

Binarycompatibility

Questions?

7 / 19

Это тоже синтаксический сахар, который надоиспользовать аккуратно (так как возможны deadlock ипроблемы производительности). Независимо отрасположения lazy val, компилируется он таким образом

c l a s s Lazy {l a z y v a l x = 1

}

impor t scala . runtime . BoxedUnit ;p u b l i c c l a s s Lazy {p u b l i c i n t x ( ) {i f ( ( bitmap$0 & 2) == 0)s y n ch r o n i z e d ( t h i s ) {i f ( ( bitmap$0 & 2) == 0) {x = 1 ;bitmap$0 = bitmap$0 | 2 ;

}BoxedUnit _tmp =BoxedUnit . UNIT ;

}r e t u r n x ;

}p u b l i c Lazy ( ) {}p r i v a t e i n t x ;p u b l i c v o l a t i l e i n t bitmap$0 ;

}

Page 8: Александр Подхалюзин, «Магия Scala изнутри»

Generated ByteCode

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

8 / 19

Page 9: Александр Подхалюзин, «Магия Scala изнутри»

Classes and names

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

9 / 19

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

✔ : ➔ $colon✔ + ➔ $plus✔ © ➔ $u00A9

Внутри Scala кода можно использовать оба вариантаподобных идентификаторовПодробнее можно изучить преобразования с помощьюметодов NameTransfomer.encode/decode изscala-compiler.jar.

Page 10: Александр Подхалюзин, «Магия Scala изнутри»

Objects

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

10 / 19

Объект всегда компилируется в два класса.

✔ Object$ содержит в себе статическое поле MODULE$,а также код всех методов

✔ Object содержит в себе все статические методыобъекта, с телом вида Object$.MODULE$.foo()

Важно понимать, что статические методы добавлены дляудобства вызова из Java. Тем не менее они добавляютсяне всегда.

✔ В Companion Trait не добавляется ничего.✔ В Companion Class не добавляются методы с таким

именем, которое уже есть в этом классе✔ Методы класса java.lang.Object

Page 11: Александр Подхалюзин, «Магия Scala изнутри»

Traits

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

11 / 19

Если trait отличается от обычного интерфейса в Javaналичием конкретных методов, то к соответствующемуинтерфейсу будет создан класс Trait$class симплементацией этих методов.

t r a i t A {v a l x = 1

de f foo ( x : Int ) = 2}

p u b l i c a b s t r a c t c l a s s A$class {p u b l i c s t a t i c i n t foo ( A $this ,

i n t x ) {r e t u r n 2 ;

}p u b l i c s t a t i c vo i d $init$ (A $this

) {$this .t e s t $ A $ _ s e t t e r _ $ x _ $ e q (1 ) ;

}}

Page 12: Александр Подхалюзин, «Магия Scala изнутри»

Trait subclasses

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

12 / 19

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

i n t foo ( ) { r e t u r n Trait$class . foo ( t h i s ) ; }

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

Page 13: Александр Подхалюзин, «Магия Scala изнутри»

Linearization

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

13 / 19

Этот алгоритм требуется компилятору, чтобы определить,в каком порядке надо искать методы в базовых классах.

t r a i t A ex t end s Bt r a i t Bc l a s s C ex t end s Bc l a s s D ex t end s C with A

Здесь порядок D, A, C, B.Если два метода с одинаковой сигнатурой безмодификатора ’override’ приходят в класс наследник изсупертрейтов, то ошибки не будет, так как обязательнонадо будет переопределить эту сигнатуру. Если же укого-то есть модификатор ’override’, может случитсянеявный override согласно правилам линеаризации.

Page 14: Александр Подхалюзин, «Магия Scala изнутри»

Initialisation order

Syntactic sugar

GeneratedByteCode

Classes and names

Objects

Traits

Trait subclasses

Linearization

Initialisation order

Binarycompatibility

Questions?

14 / 19

Инициализация происходит в порядке, обратномлинеаризации. Если к переменной обратиться до того, какона проинициализирована, можно получить NPE.Есть два способа избавится от подобных проблем:

✔ Использовать lazy val✔ Использовать early definitions

Page 15: Александр Подхалюзин, «Магия Scala изнутри»

Binary compatibility

Syntactic sugar

GeneratedByteCode

Binarycompatibility

Methods

Values

Lazy values

Questions?

15 / 19

Page 16: Александр Подхалюзин, «Магия Scala изнутри»

Methods

Syntactic sugar

GeneratedByteCode

Binarycompatibility

Methods

Values

Lazy values

Questions?

16 / 19

Здесь все очень похоже на Java. Чаще всего проблем небудет.

✔ Добавлять метод можно даже в Trait, но только еслистарые методы не ссылаются на новые, так какимплементации в классы наследники добавлены небудут.

✔ В класс опасно добавлять методы, имена которыхсовпадают с именем методов из Companion Object, таккак статические реализации будут удаленыкомпилятором, что может вызвать проблемы с кодомнаписанным не на Scala.

Page 17: Александр Подхалюзин, «Магия Scala изнутри»

Values

Syntactic sugar

GeneratedByteCode

Binarycompatibility

Methods

Values

Lazy values

Questions?

17 / 19

Если переменная находится в классе или объекте, топеременная компилируется в getter, setter (для variables) иfield, который инциализируется в конструкторе класса. Азначит все будет также хорошо как и с методами.Если переменная находится в Trait, то она компилируетсяв getter и setter (всегда). Инициализация происходит вметоде Trait$class.$init$ через вызов setter.

✔ Значит добавлять переменную в трейт нельзя. Так какбудет вызван абстрактный setter.

✔ По аналогичным причинам нельзя изменять def на val✔ Тем не менее val на def также изменять нельзя, так как

тогда в классах наследниках, где как раз и хранитсяfield, переменная не будет проинициализирована.

Page 18: Александр Подхалюзин, «Магия Scala изнутри»

Lazy values

Syntactic sugar

GeneratedByteCode

Binarycompatibility

Methods

Values

Lazy values

Questions?

18 / 19

В подклассах не генерируется field bitmap, если он есть вбазовом классе.

✔ Поэтому в этих случаях добавлять lazy value в классынельзя

✔ В Trait добавлять можно, если эта переменная небудет использована в старом коде

✔ Так как компиляция lazy val в Trait от компиляцииметода не отличается совсем, то их можно заменятьдруг на друга, учитывая только, что в наследникахподобная семантика не изменится

Page 19: Александр Подхалюзин, «Магия Scala изнутри»

Questions?

Syntactic sugar

GeneratedByteCode

Binarycompatibility

Questions?

19 / 19