SlideShare uma empresa Scribd logo
1 de 77
Baixar para ler offline
Опыт использования
Scala на Android
Михаил Трищенков
Что же я наделал?
Scala на Android
Транспорт Новосибирска
3
Транспорт Новосибирска
4
Транспорт Новосибирска
https://play.google.com/store/apps/details?id=net.kriomant.gortrans
https://github.com/kriomant/gortrans
〉
〉
5
О боже, но почему?
Scala на Android
Но почему?
Вывод типов
Локальные функции и типы
Кортежи и прочая функциональщина
«==» работает как положено
〉
〉
〉
〉
7
Анонимные функции
Анонимные функции
9
class Items {
def mapSum(func: Item => Int): Int = {
var sum = 0
for (item <- this.items)
sum += func(item)
sum
}
}
mapSum(item => item.weight)
mapSum(_.weight)
mapSum { item =>
item.weight
}
Анонимные функции
Ну лямбды, а где их использовать?
10
val shownRoutes = infoWindowMarkers
.filter(_.isShown)
.map { m =>
val info = vehicleMarkers(m).info
info.routeId
}
Анонимные функции
Больше сахара!
11
val shownRoutes = for {
m <- infoWindowMarkers
if m.isShown
info = vehicleMarkers(m).info
} yield info.routeId
Анонимные функции
Изменяемый scope
12
val items = Array(1, 2, 4)
var sum = 0
items.foreach { item =>
sum += item
}
Безопасность
Java: null
14
Route getRoute(String name, Integer order);
String getSchedule(String route) {
Route route = getRoute("13a", null);
String schedule = null;
if (route = null) { schedule = route.getSchedule() }
return schedule;
}
Scala: Option
15
def getRoute(name: String, order: Option[Int]): Option[Route]
def getSchedule(route: String): Option[String] = {
getRoute(route, None).map(_.schedule)
}
for {
first <- getOptValue1()
if first >= 0
second <- getOptValue2()
} yield first + second
Java: read-only collections
16
List<String> getStops() {
return Collections.unmodifiableList(this.stops);
}
List<String> list = route.getStops();
list.add("stop");
// BOOM! UnsupportedOperationException
List<String> getStops() {
return new ArrayList<String>(this.stops);
}
Scala: read-only interface / immutable collection
17
def getStops: collection.Seq[String] = stops
val stops = route.getStops
stops += "stop"
// error: value += is not a member of Seq[String].
val extendedStops = stops :+ "stop"
Scala: immutable by default
18
collection.List
collection.immutable.List = List
collection.mutable.List
Scala: immutable by default
val
immutable collections
case classes
〉
〉
〉
19
Case classes &
pattern matching
Simple Java class
21
class File {
String name;
long size;
bool executable;
File(String name, long size, bool executable) {
this.name = name;
this.size = size;
this.executable = executable;
}
@Override bool equals(Object other) { … }
…
Initializers
22
class File(name: String, size: Long, executable: Boolean) {
def getName: String = name
def getSize: Long = size
def isExecutable: Boolean = executable
}
val file = new File("autoexec.bat", 1337, false)
println(file.getName)
Initializers
23
class File(val name: String,
val size: Long,
val executable: Boolean)
val file = new File("autoexec.bat", 1337, false)
println(file.name)
Case class
24
case class File(name: String, size: Long, executable: Boolean)
val file = File("autoexec.bat", 1337, false)
println(file.name)
val updated = file.copy(size = 1400)
file match {
case File("autoexec.bat", _, _) => nostalgia.on()
case File(name, _, _) => ordinary_file()
}
Traits
Mixins: добавляем NavigationDrawer
26
Mixins: добавляем NavigationDrawer
27
@Override public void onCreate(Bundle savedInstanceState) {
mDrawerToggle = new ActionBarDrawerToggle(…);
}
@Override protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerToggle.syncState();
}
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Mixins: добавляем NavigationDrawer
28
trait HavingSidebar extends Activity {
// Interface
var drawerLayout: Int
def onDrawerShown(): Unit
def onDrawerHidden(): Unit
// Implementation
override def onCreate(savedInstanceState: Bundle): Unit = {
super.onCreate(savedInstanceState)
mDrawerToggle = new ActionBarDrawerToggle(…)
}
var mDrawerToggle: ActionBarDrawerToggle = null
…
Mixins: добавляем NavigationDrawer
29
class MyActivity extends Activity with HavingSidebar {
var drawerLayout: Int = R.layout.drawer
override def onDrawerShown(): Unit = { … }
…
}
Curried functions +
Call-by-name evaluation
Curried function: closing
31
type Closable = {def close()}
def closing[R <: Closable, T](resource: R)(block: R => T): T = {
try {
block(resource)
} finally {
if (resource != null)
resource.close()
}
}
closing(db.query(…)) { cursor => … }
Curried function: edit
32
def edit(sp: SharedPreferences)
(block: SharedPreferences.Editor => Unit) = {
val editor = sp.edit()
block(editor)
editor.commit()
}
edit(prefs) { editor =>
editor.putLong(…)
editor.remove(…)
}
Call-by-name-evaluation: retry
33
def retryOnceIfEmpty(f: => String): String = {
val res = f
if (res.nonEmpty) res else f
}
val jsonStr = retryOnceIfEmpty {
client.fetchRouteJson(id)
}
Макросы
Макросы: sqlite.insert
35
def addEvent(timestamp: Date,
kind: String,
ref: Option[Long],
data: String): Long = {
import EventsTable.Columns._
val values = new ContentValues
values.put(DATE_TIME, timestamp.getTime)
values.put(KIND, kind)
values.put(REF, ref.orNull)
values.put(DATA, data)
db.insertOrThrow(EventsTable.NAME, null, values)
}
Макросы: sqlite.insert
36
def addEvent(timestamp: Date,
kind: String,
ref: Option[Long],
data: String): Long = {
import EventsTable.Columns._
EventsTable.insert(
DATE_TIME -> timestamp.getTime,
KIND -> kind,
REF -> ref,
DATA -> data
)
}
Макросы: inject view
37
class MyActivity extends AppCompatActivity {
override def onCreate(savedInstanceState: Bundle): Unit = {
super.onCreate(savedInstanceState)
setContentView(R.layout.account_list_activity)
AutoInjector.injectViews()
…
}
@InjectView(R.id.account_list)
private[this] var accountList: ListView = _
}
Макросы: inject view
38
def injectViewsImpl(c: Context)(): c.Expr[Unit] = {
val selfClass = c.internal.enclosingOwner.asMethod.owner.asType
val theType = selfClass.toType
val injections = for {
decl <- theType.decls
annotation <- decl.annotations.find { a =>
a.tree.tpe =:= c.weakTypeOf[InjectView]
}
} yield {
...
q"""this.$decl = this.findViewById($id).asInstanceOf[$viewType]"""
}
c.Expr[Unit](q"{..$injections}")
}
Implicits
Implicits: преобразование типов
40
okButton.setOnClickListener(new OnClickListener {
def onClick(view: View) { doSmth() }
})
Implicits: преобразование типов
41
implicit class Function0ToOnClickListener(f: () => Unit)
extends OnClickListener {
override def onClick(v: View): Unit = f()
}
okButton.setOnClickListener { () => doSmth() }
Implicits: extension methods
42
implicit class ViewExt(view: View) {
override def onClick(block: => Unit): Unit = {
view.setOnClickListener(new OnClickListener {
def onClick(v: View) { block }
})
}
}
okButton.onClick { doSmth() }
Implicits: неявные параметры
43
def du(arg: Int, logger: Logger): Foo = { … }
def duHast(arg: String, logger: Logger): Bar = { … }
def duHastMich(foo: Foo, bar: Bar,
logger: Logger): Baz = { … }
val logger = Logger(…)
val baz = duHastMich(du(2, logger),
duHast("yes", logger),
logger)
Implicits: неявные параметры
44
def du(arg: Int)(implicit logger: Logger): Foo = { … }
def duHast(arg: String)
(implicit logger: Logger): Bar = { … }
def duHastMich(foo: Foo, bar: Bar)
(implicit logger: Logger): Baz = {…}
implicit val logger = Logger(…)
val baz = duHastMich(du(2), duHast("yes"))
Как собирать?
Scala на Android
Как собирать?
46
SBT
Стандарт для Scala-проектов
http://www.scala-sbt.org
Есть сторонний Android-плагин
〉
〉
47
SBT + Android
Плагины:
https://github.com/jberkel/android-plugin (устарел)
https://github.com/pfn/android-sdk-plugin
〉
〉
48
SBT
49
Перегрузка операторов
Ленивые вычисления
Лямбды
Макросы
Пример конфигурации SBT
50
lazy val core = Project("core", file("core"),
settings = Defaults.defaultSettings ++ Seq(
apiLevel := 15,
platformName in Android :=
s"android-${(apiLevel in Android).value}",
useProguard in Android := true,
proguardOption in Android ~= { _ + " -dontnote scala.** " }
libraryDependencies +=
"org.ccil.cowan.tagsoup" % "tagsoup" % "1.2",
…
Gradle
Популярная система сборки для Java и
не только
http://gradle.org
Android-плагин поддерживается Google
Есть сторонний Scala-плагин
〉
〉
〉
51
Gradle
http://developer.android.com/intl/ru/tools/building/plugin-for-
gradle.html
https://github.com/saturday06/gradle-android-scala-plugin
52
Конфигурация Gradle
53
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
classpath 'jp.leafytree.gradle:gradle-android-scala-plugin:1.4'
}
}
apply plugin: 'com.android.application'
apply plugin: 'jp.leafytree.android-scala'
android {
compileSdkVersion 'android-23'
buildToolsVersion '23.0.1'
defaultConfig {
А как же родимый Java-код,
либы, вот это все?
Scala на Android
Поддержка IDE
Scala на Android
IntelliJ IDEA
Поддерживает SBT-проекты
Поддерживает Gradle-проекты
Официальная IDE для Android
Официальный Scala-плагин от JetBrains
〉
〉
〉
〉
56
Небось отладка сломается?
IntelliJ IDEA
А рефакторинг?
IntelliJ IDEA
IntelliJ IDEA: радости
Автодополнение, включая implicits
Отладка, поддерживается Scala-специфика
Рефакторинг
Fast Scala Compiler — демон компиляции
〉
〉
〉
〉
62
IntelliJ IDEA: печали
Сброс настроек, не поддерживаемых системой сборки
Scala SDK
Настройки путей
〉
〉
63
За что мне все это?
Scala на Android
Переопределение vararg-методов
https://issues.scala-lang.org/browse/SI-1459
65
public abstract class AsyncTaskBridge<Progress, Result>
extends AsyncTask<Void, Progress, Result> {
@Override
public Result doInBackground(Void... params) {
return doInBackgroundBridge();
}
protected abstract Result doInBackgroundBridge();
…
Доступа к protected static членам
https://issues.scala-lang.org/browse/SI-1806 (закрыт)
ItemizedOverlay, View.mergeDrawableStates
66
abstract public class ViewStaticBridge extends View {
public ViewStaticBridge(Context context) { super(context); }
public static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
return View.mergeDrawableStates(baseState, additionalState);
}
}
Parcellable::CREATOR
67
case class User(age: Int, name: String) extends Parcelable {
protected this(source: Parcel) =
this(source.readInt(), source.readString())
override def describeContents() = 0
override def writeToParcel(destination: Parcel, flags: Int) { … }
}
object User
{
override val CREATOR = new Parcelable.Creator[User] {
override def createFromParcel(source: Parcel) = new User(source)
override def newArray(size: Int) = new Array[User](size)
}
}
65K Reference Limit
Scala поможет быстрее достичь высот!
68
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
65K Reference Limit
Стандартная библиотека: много классов, много методов;
в 2.10 стандартную библиотеку разделили
ProGuard нужен даже для debug-сборок
Multi-DEX — не использовал
linearAlloc bug
Размер gortrans — 1,5 Мб
〉
〉
〉
〉
69
ProGuard
Требуется несколько настроек, чтобы не вырезались
нужные части стандартной библиотеки
〉
70
-dontwarn scala.concurrent.forkjoin.**
-dontwarn scala.concurrent.impl.AbstractPromise
-dontwarn scala.concurrent.util.Unsafe
-dontwarn scala.reflect.**
# See https://issues.scala-lang.org/browse/SI-5397
-keep class scala.collection.SeqLike {
public protected *;
}
…
Скорость компиляции
Scala компилирует медленнее Java в 2-10 раз
Частично проблема решается FSC (Fast Scala Compiler
daemon) и sbt incremental compilation
Усугубляется необходимостью запуска ProGuard (можно
решить предустановкой Scala в эмуляторе)
〉
〉
〉
71
Потребление ресурсов
Scala провоцирует использовать много Closures
Но никто не мешает использовать Java-like код в
критичных точках
Макросы позволяют создавать zero-cost абстракции
〉
〉
72
Библиотечные зависимости
Source-level совместимость между минорными версиями
Scala
Требуется пересборка библиотек для каждой минорной
версии
〉
〉
73
Заключение
Scala на Android
Что мы получаем
Более выразительный язык
Более типизированный
Компактный код
Дополнительные возможности переиспользования кода
〉
〉
〉
〉
75
Чем за это расплачиваемся
Медленная сборка
Поддержка новых возможностей системы сборки Android
запаздывает
〉
〉
76
Альтернативы
Java 8 lambdas for Java 5–7
https://github.com/orfjackal/retrolambda
Kotlin
http://kotlinlang.org/docs/tutorials/kotlin-android.html
http://kotlinlang.org/docs/tutorials/android-plugin.html
android-apt + javapoet (used by Butterknife)
guava / apache-commons
〉
〉
〉
〉
77

Mais conteúdo relacionado

Mais procurados

Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Roman Brovko
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonPython Meetup
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.Roman Brovko
 
Очень вкусный фрукт Guava
Очень вкусный фрукт GuavaОчень вкусный фрукт Guava
Очень вкусный фрукт GuavaEgor Chernyshev
 
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Roman Brovko
 
Stream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководовStream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководовtvaleev
 
Developing highload servers with Java
Developing highload servers with JavaDeveloping highload servers with Java
Developing highload servers with JavaAndrei Pangin
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6Dmitry Soshnikov
 
5. java lecture io
5. java lecture io5. java lecture io
5. java lecture ioMERA_school
 
Объектное и прототипное программирование в Javascript
Объектное и прототипное программирование в JavascriptОбъектное и прототипное программирование в Javascript
Объектное и прототипное программирование в JavascriptDenis Latushkin
 
Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Alexander Granin
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеPython Meetup
 
Convert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at VizorConvert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at VizorDevGAMM Conference
 
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Platonov Sergey
 
Игорь Лабутин «Коллекционируем данные в .NET»
Игорь Лабутин «Коллекционируем данные в .NET»Игорь Лабутин «Коллекционируем данные в .NET»
Игорь Лабутин «Коллекционируем данные в .NET»SpbDotNet Community
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.Roman Brovko
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILRoman Brovko
 

Mais procurados (19)

Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.
 
Очень вкусный фрукт Guava
Очень вкусный фрукт GuavaОчень вкусный фрукт Guava
Очень вкусный фрукт Guava
 
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.
 
Stream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководовStream API: рекомендации лучших собаководов
Stream API: рекомендации лучших собаководов
 
Developing highload servers with Java
Developing highload servers with JavaDeveloping highload servers with Java
Developing highload servers with Java
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6
 
5. java lecture io
5. java lecture io5. java lecture io
5. java lecture io
 
Объектное и прототипное программирование в Javascript
Объектное и прототипное программирование в JavascriptОбъектное и прототипное программирование в Javascript
Объектное и прототипное программирование в Javascript
 
Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгирование
 
Convert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at VizorConvert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at Vizor
 
Generics
GenericsGenerics
Generics
 
Thread
ThreadThread
Thread
 
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
 
Игорь Лабутин «Коллекционируем данные в .NET»
Игорь Лабутин «Коллекционируем данные в .NET»Игорь Лабутин «Коллекционируем данные в .NET»
Игорь Лабутин «Коллекционируем данные в .NET»
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GIL
 

Semelhante a Scala on android

Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?Vasil Remeniuk
 
Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)Dmitry Stropalov
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Яковенко Кирилл
 
Why Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreWhy Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreAlexander Schepanovski
 
Инструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщикаИнструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщикаSQALab
 
Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Alex Ott
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановYandex
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3Technopark
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Yandex
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8chashnikov
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6Technopark
 
Характерные черты функциональных языков программирования
Характерные черты функциональных языков программированияХарактерные черты функциональных языков программирования
Характерные черты функциональных языков программированияAlex.Kolonitsky
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в DjangoMoscowDjango
 
Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''
Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''
Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''OdessaJS Conf
 

Semelhante a Scala on android (20)

Scala for android
Scala for androidScala for android
Scala for android
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 
Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)Scala and LiftWeb presentation (Russian)
Scala and LiftWeb presentation (Russian)
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
 
Why Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreWhy Every Language Needs Its Underscore
Why Every Language Needs Its Underscore
 
Bytecode
BytecodeBytecode
Bytecode
 
Инструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщикаИнструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщика
 
Kotlin
KotlinKotlin
Kotlin
 
Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)
 
Статический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий ЛевановСтатический и динамический полиморфизм в C++, Дмитрий Леванов
Статический и динамический полиморфизм в C++, Дмитрий Леванов
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
Характерные черты функциональных языков программирования
Характерные черты функциональных языков программированияХарактерные черты функциональных языков программирования
Характерные черты функциональных языков программирования
 
Decorators' recipes
Decorators' recipesDecorators' recipes
Decorators' recipes
 
C sharp deep dive
C sharp deep diveC sharp deep dive
C sharp deep dive
 
C# Deep Dive
C# Deep DiveC# Deep Dive
C# Deep Dive
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
 
Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''
Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''
Denys Samoylenko ''JS learning lifehacks: common programmer's mistake''
 

Scala on android