Scala медленные сборки: подходы к разработке, чтобы избежать
Прежде всего, инкрементные сборки через SBT довольно удивительны, как правило, в < 1сек. Однако иногда вам приходится делать полную очистку/компиляцию, или, в случае инкрементных сборок, вы вносите изменения в один файл, который затем запускает сборку из десятков других файлов.
В этом случае развитие Scala становится меньше... потеха, поскольку в результате замедления рабочего потока можно стимулировать переключение контекста (проверить электронную почту, последние потоки Stackoverflow и т.д.), что тонко делает менее продуктивным
Итак, какие подходы к разработке следует избегать, чтобы улучшить полные чистые/компилируемые сборки и (в идеале), инкрементные сборки change-one-file-without-recompiling-half-the-application?
Примеры, о которых я могу думать:
1) лучше ли иметь файл с тысячей + строк do-it-all Scala или несколько разделенных файлов?
2) Могу ли я получить свой пирог (рисунок) или будет ли это увеличивать время сборки?
3) Могу ли я иметь pimp'd x, y, z библиотеку или лучше найти другой способ?
4) являются объектами пакета (с имплицитами) убийца времени сборки?
5) вложенные объекты и черты?
6) неявные методы/параметры или перестать быть умными и быть явными?
Конкретно, я подумываю о том, чтобы отбросить образец торта DAO, который я придумал и объединил в класс Case + класс компаньона ScalaQuery + минимальную базу данных базы данных. Только это потеряет 20 файлов Scala.
Приложение достаточно мало (120 Scala + 10 java файлов) для рефакторинга сейчас без лишних хлопот. Очевидно, что как приложение Scala растет, так же как и время сборки, основано только на LOC. Я просто пытаюсь понять, где можно обрезать жир и где не беспокоиться (т.е. Держать вещи такими, какие они есть), поэтому текущие и будущие приложения извлекают выгоду из выразительности, которую Scala дает без лишнего раздувания времени сборки.
Спасибо за некоторые примеры вашего опыта хорошего, плохого и уродливого развития Scala в зависимости от времени сборки.
Ответы
Ответ 1
Посмотрите как работает инкрементная перекомпиляция в SBT.
Это примерно так:
- Найти все классы, чьи общедоступные API были изменены
- Недействительные все его иждивенцы, иждивенцы иждивенцев и т.д.
Для целей SBT "зависимый" является одновременно пользователем класса и класса, определенного в том же файле.
Пример Owen для foo.scala может даже быть таким, и вы увидите проблему:
object foo {
def z: Int = 8
}
object foo2 {
class A { ... }
}
Хорошая практика:
- Отдельные файлы для отдельных классов
- Мелкозернистые интерфейсы
- Использовать тот же уровень абстракции в сопутствующих объектах, что и в их классах-компаньонах; если объект-компаньон достигает уровня абстракции, потяните его в отдельный класс и файл.
Ответ 2
Я заметил, что члены типа могут принудительно восстанавливать места, которых вы не ожидали. Например:
foo.scala:
object foo {
class A {
type F = Float
}
def z: Int = 8
}
bar.scala:
object bar {
def run { println(foo.z) }
}
Изменение значения z
не приводит к перекомпиляции bar
. Изменение типа F
выполняется, хотя bar
никогда не ссылается на F
или даже на A
. Почему, я понятия не имею (Scala 2.9.1).