implementation of 'go'-like language constructions in scala (russian)
1. Go / Scala
Реализация конструкций языка Go в Scala
Руслан Шевченко
<ruslan@shevchenko.kiev.ua>
https://github.com/rssh/scala-gopher
2. Что такое в go ?
области видимости (scope)
( defer/ panic / destroy )
параллелизм
channels
select statement
Отличие от 'родного' подхода в scala
Как реализовать 'go' вариант
Что дальше ...
3. С defer
def copy(inf: File, outf: File): Long = goScope {
val in = new FileInputStream(inf)
defer{ in.close() }
val out = new FileOutputStream(outf);
defer{ out.close() }
out.getChannel() transferFrom(in.getChannel(), 0, Long.MaxValue)
}
def copy(inf: File, outf: File): Long =
{
val in = new FileInputStream(inf)
try {
val out = new FileOutputStream(outf)
try {
out.getChannel() transferFrom(in.getChannel(), 0, Long.MaxValue)
} finally {
out.close();
}
} finally {
in.close();
}
}
По старому:
4. Традиционно:
def copy(inf: File, outf: File): Long =
{
val in = new FileInputStream(inf)
try {
val out = new FileOutputStream(out)
try {
out.getChannel() transferFrom(in.getChannel(), 0,Long.MaxValue)
} finally {
out.close();
}
} finally {
in.close();
}
}
5. def copy(inf: File, outf: File): Long = goScope {
val in = new FileInputStream(inf)
defer{ in.close() }
val out = new FileOutputStream(outf);
defer{ out.close() }
out.getChannel() transferFrom(in.getChannel(), 0, Long.MaxValue)
}
Как в go
7. Как это реализовано в scala:
Макросы
Передача параметров по имени
Традиционный try/cath
goScope = .... (macro, в котором
переписывается аргумент)
defer => @compileTimeOnly
8. def copy(inf: File, outf: File): Long = {
val sc0 = new ScopeContext();
sc0.eval {
val in = new FileInputStream(inf)
sc0.pushDefer{ in.close() }
val out = new FileOutputStream(outf);
sc0.pushDefer{ out.close() }
out.getChannel() transferFrom(in.getChannel(), 0, Long.MaxValue)
}
sc0.unwindDefers[Long]()
}
Без сахара:
14. go func() {
for {
select {
case msg1 := <- c1:
fmt.Println(msg1)
case msg2 := <- c2:
fmt.Println(msg2)
}
}
}()
Go: типичный паттерн:
select внутри for внутри goroutine
15. go
for(s <- select) s match {
case c1 ~> (msg1: Msg1Type) => println(msg1)
case c2 ~>(msg2:Msg2Type) => println(msg2)
}
Scala аналог:
- реализован как макрос foreach
- ~> в образце: надо указывать тип
- активная очередь
Открытый вопрос: хороший “родной” не-макро синтаксис
16. Без сахара (упрощенно) [1]:
go {
val s = new SelectContext();
s.addReader(c1) {
(msg1:Msg1Type) => println(msg1)
}
s.addReader(c2) {
(msg2:Msg2Type) => println(msg2)
}
s.run()
}
17. Без сахара (упрощенно) [2]:
val scope = new ScopeContext();
val s = new SelectContext();
Future {
s.addReader(c1) {
(msg1:Msg1Type) => println(msg1)
}
s.addReader(c2) {
(msg2:Msg2Type) => println(msg2)
}
} map { s.runUnblocked() }
map { scope.finish() }
(Дальнейшая работа)
S
18. Select вне цикла:
for(s <- select.once) s match {
case c1 ~> (msg1: Msg1Type) => println(msg1)
case c2 ~>(msg2:Msg2Type) => println(msg2)
}
select {
case msg1 := <- c1: fmt.Println(msg1)
case msg2 := <- c2: fmt.Println(msg2)
}
GO
SCALA
19. Текущая внутренняя реализация:
Активная очередь
Channel = [Blocked Query + Execution Context ]
• Select: координационные примитивы + wait [oops...]
– Count down latch
– Memory barrier
Расширения по сравнению с моделью Go
Асинхронные операции
Остановка контекста (context shutdown).
20. val consumer = go {
for(s <- select) {
s match {
case `channel` ~> (i:Int) =>
sum = sum + i
if (i==1000) s.shutdown()
}
}
sum
}
Future[Int]
21. Akka
(как в Erlang)
Channels (как в go)
Реактивные
Неблокирующие
операции
Запрос/Ответ
парадигма
При перегрузке -
mailbox overflow при send
Что-то обработать.
Потоково ориентированные
Wait это нормально
(переключение потоков в это время)
Однонаправленная труба
При перегрузке
ожидание при send
Что-то посчитать
22. x1, x2, x3, ............
x1*y1, x2*y2, x3*y3, ............
y1, y2, y3, ............
go {
while(;) {
val x = channelX ?
val y = channelY ?
channelZ ! x*y
}
}
Пример: Легко с gopher, сложно с Akka
24. Что дальше
Новый “базис”
[select in loop in go] - слишком фундаментально,
что бы быть комбинацией 3-х конструкций.
Альтернативный синтаксис
с явной реактивностью
Заставить работь вывод типов
Подключаемые (pluggable) реализации
Оптимизировать крайние случаи
25. Подключаемые реализации.
Существует много стратегий.
(текущая реализация не лучшая)
– Schedule/callback вместо waits
– Собственный dispatcher
– .....
Тесты производительности,
Цикл “прототипирование / тест”
Проблемы
- отсутсвие механизма импорта модулей
@exported import
- JVM
Помощь сообщества (?)
27. go {
...
val x = channelX ?
val y = channelY ?
channelZ ! x*y
...
}
go { .....
channelX.read map { x =>
channelY.read map { y =>
channelZ.write map { ch =>
ch <~! x*y
} } }
}
}
28. someCollection foldLeft {
(s,x) => s + (x* channel ?)
}
Такая же проблема, как и с CPS:
- foldLeft не наш.
- foreach не наш и непонятно, что там
for(x <- something) channel <~~ x
отсуствие поддержки сопрограмм в JVM
29. Поддержка сопрограм в JVM
- Изменение JVM
- Переписывание байт-кода
- Ничего не делать – thread pool большой
(нагрузить wait выполнением
заведомо неблокирующих задач)
30. The Da Vinci machine
http://openjdk.java.net/projects/mlvm/index.html
AVIAN VM
http://oss.readytalk.com/avian/