Ответ 1
sbt 0.12 синтаксис против синтаксиса sbt 0.13
Есть ли значимая разница между этими двумя?
По смысловой разнице, если вы подразумеваете семантическую разницу, как в наблюдаемой разнице в поведении скомпилированного кода, они одинаковы.
Если вы имеете в виду какие-либо намеченные различия в коде, это о разности стилей между синтаксисом sbt 0.13 синтаксиса sbt 0.12. Концептуально, я думаю, синтаксис sbt 0.13 упрощает изучение и кодирование, поскольку вы напрямую относитесь к T
вместо Initialize[T]
. Используя макрос, sbt 0.13 расширяет x.value
на эквивалент sbt 0.12.
анатомия <= </h2 >
Я пытаюсь понять, зачем ему нужен вызов карты.
Фактически, один из макросов разницы теперь способен обрабатывать автоматически.
Чтобы понять, почему map
необходим в стиле sbt 0.12, вам нужно понять тип выражения DSL sbt, которое Setting[_]
. Как Руководство по началу работы:
Вместо этого определение сборки создает огромный список объектов с типом
Setting[T]
, гдеT
- тип значения на карте. ASetting
описывает преобразование в карту, например, добавление новой пары ключ-значение или добавление к существующему значению.
Для задач тип выражения DSL Setting[Task[T]]
. Чтобы повернуть ключ настройки в Setting[T]
или включить ключ задачи в Setting[Task[T]]
, вы используете метод <<=
, определенный на соответствующих клавишах. Это реализовано в Structure.scala (база кода sbt 0.12 имеет более простую реализацию <<=
, поэтому я буду использовать это как ссылку. ):
sealed trait SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] with Scoped.ScopingSetting[SettingKey[T]] with Scoped.DefinableSetting[T] with Scoped.ListSetting[T, Id] { ... }
sealed trait TaskKey[T] extends ScopedTaskable[T] with KeyedInitialize[Task[T]] with Scoped.ScopingSetting[TaskKey[T]] with Scoped.ListSetting[T, Task] with Scoped.DefinableTask[T] { ... }
object Scoped {
sealed trait DefinableSetting[T] {
final def <<= (app: Initialize[T]): Setting[T] = setting(scopedKey, app)
...
}
sealed trait DefinableTask[T] { self: TaskKey[T] =>
def <<= (app: Initialize[Task[T]]): Setting[Task[T]] = Project.setting(scopedKey, app)
...
}
}
Обратите внимание на типы параметров app
. Клавиша настройки <<=
принимает Initialize[T]
, тогда как клавиша задачи <<=
принимает Initialize[Task[T]]
. Другими словами, в зависимости от типа lhs выражения <<=
изменяется тип rhs. Это требует, чтобы пользователи sbt 0.12 знали о разности настроек/задач в ключах.
Предположим, что у вас есть ключ настройки, например description
на lhs, и предположим, что вы хотите зависеть от name
и создать описание. Чтобы создать выражение зависимости параметра, вы используете apply
:
description <<= name { n => n + " is good." }
apply
для одного ключа реализовано в Settings.scala:
sealed trait Keyed[S, T] extends Initialize[T]
{
def transform: S => T
final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform)
}
trait KeyedInitialize[T] extends Keyed[T, T] {
final val transform = idFun[T]
}
Далее, вместо description
, предположим, что вы хотите создать параметр для jarName in assembly
. Это ключ задачи, поэтому rhs <<=
принимает Initialize[Task[T]]
, поэтому apply
не является хорошим. Здесь map
входит:
jarName in assembly <<= name map { n => n + ".jar" }
Это реализовано в Structure.scala:
final class RichInitialize[S](init: Initialize[S]) {
def map[T](f: S => T): Initialize[Task[T]] = init(s => mktask(f(s)) )
}
Поскольку ключ установки расширяет KeyedInitialize[T]
, который равен Initialize[T]
, и потому что там неявное преобразование от Initialize[T]
до RichInitialize[T]
, это доступно для name
. Это нечетный способ определения map
, так как карты обычно сохраняют структуру.
Это может иметь больше смысла, если вы видите аналогичный класс обогащения для ключей задач:
final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] {...}
sealed abstract class RichInitTaskBase[S, R[_]] {
def map[T](f: S => T): Initialize[R[T]] = mapR(f compose successM)
}
Итак, для задач map
отображает из задачи типа S
в T
. Для настроек мы можем думать об этом как: map
не задано в настройке, поэтому он неявно преобразуется в задачу и отображает ее. В любом случае, это позволит пользователям sbt 0.12 подумать: используйте apply
для настроек, map
для задач. Обратите внимание, что apply
всегда исчезает для ключей задач, поскольку они расширяют Keyed[Task[T], Task[T]]
. Это должно объяснить:
sbt-hello/build.sbt:21: error: type mismatch;
found : Unit
required: sbt.Task[Unit]
Тогда возникает проблема с кортежем. До сих пор я обсуждал зависимости к одной настройке. Если вы хотите больше зависеть, sbt неявно добавляет apply
и map
в Tuple2..N
для его обработки. Теперь он расширился до 15, но он использовался только до Tuple9
. Увидев с новой точки зрения пользователя, идея вызова map
в Tuple9
настроек, чтобы он генерировал задачу типа Initialize[Task[T]]
, выглядел бы как бы чужой. Без изменения основного механизма sbt 0.13 обеспечивает намного более чистую поверхность, чтобы начать работу.