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).