I recently got instructed to not confuse tasks and commands on this answer, which made me first realize that there even exist a difference. During my research confusion raised even more and I must admit that I am not clearly able to separate both! I think the main problem is that the terminology is often used synomymously but the concepts are different, highly related and quite similar to some extend. Reading the documentation didn't give me much satisfaction. I don't want to explicitly show problems in the sbt documentation, so don't get me wrong about this, but I want you to see my current progress. I have marked my questions bold during the journey and added a number in front of it.
The first resource I consulted was the Tasks and Commands section in the documentation, which only pointed to the getting started guide.
The getting started guide doesn't really explain much about the differences on this exact behalf.
In particular the Defining tasks and settings section seem to introduce even more confusion, with the destinction between the types; Setting[T]
, Setting[Task[T]]
, Task[T]
and the terminology of keys and its corresponding types.
A
TaskKey[T]
is said to define a task. Tasks are operations such ascompile
orpackage
. They may returnUnit
(Unit is Scala forvoid
), or they may return a value related to the task, for examplepackage
is aTaskKey[File]
and its value is the jar file it creates.
This is a bit quirky but ok for now, thus Tasks are TaskKey
instances with a result of type T
.
Each time you start a task execution, for example by typing
compile
at the interactive sbt prompt, sbt will re-run any tasks involved exactly once.
So any task is available on the sbt prompt. So where is the difference to a command? On further sections both seem to be used synonymously, like here. The section More About Settings describes further:
Remember that some settings describe tasks, so this approach also creates dependencies between tasks.
So tasks may depend on each other, introduced by settings.
A plugin extends the build definition, most commonly by adding new settings. The new settings could be new tasks. For example, a plugin could add a codeCoverage task which would generate a test coverage report.
Plugins may introduce new tasks using settings.
Also remember from .sbt build definition that a setting has a fixed value until project reload, while a task is re-computed for every “task execution” (every time someone types a command at the sbt interactive prompt or in batch mode).
This makes me think a command is only the thing entered on the sbt prompt or directly to the terminal using batch mode. Further it arises an idea that the command only acts as a shallow front-end for each task. #1 Does every task has a corresponding command?
By defining triggered plugins, auto plugins can be used as a convenient way to inject custom tasks and commands across all subprojects.
Here I think commands may be setup separately - similar as tasks. However the section Running Commands talks about creating aliases for commands or tasks but doesn't tell anything about tasks. Again I feel like are these concepts may be the same, although I know both are different.
Here is a list of features pertaining to tasks according the Tasks page:
Above the features it further says:
Settings are evaluated at project load time. Tasks are executed on demand, often in response to a command from the user.
Ok, so some tasks may be initiated by other means.
Finally the command page states:
A “command” looks similar to a task: it’s a named operation that can be executed from the sbt console.
So for now I think any call to a specified task or command coming from the sbt prompt or batch is being called a command. No matter if the command directs to a task or a command instance. #2 Thus does it make sense to distinguish between a definition and a call level, in order to reduce ambigouties? For instance: On the call level everything is a command, but on the definition level this is a Command
or a TaskKey
instance.
Here is a piece of code showing the general anatomy of a command definition:
val action: (State, T) => State = ???
val parser: State => Parser[T] = ???
val command: Command = Command("name")(parser)(action)
So the command
's action
is a state transition which makes it highly composable in terms of functional programming. In comparison to that a task being a TaskKey[T]
instance works more like a simple function returning a result of type T
. Point 3 in the feature list states that tasks produce values which makes me think of tasks more like pure-ish functions producing no to only some small side effects like logging as stated by point 6, except for tasks with return type of Unit
.
However, a command’s implementation takes as its parameter the entire state of the build (represented by State) and computes a new State. This means that a command can look at or modify other sbt settings, for example. Typically, you would resort to a command when you need to do something that’s impossible in a regular task.
So I get the idea of a command being somewhat superior for some cases. Then I ask myself: Do tasks and commands share the same subset of features regarding the list of task features, like the obvious point 2? Point 1 states that arbritrary modifications to settings can take place due to the integration of the settings system, isn't that true for commands as well? Same for point 4, 5 and 6. #3 Can someone clarify this, especially the limitations and give reasons for when I should should objectively prefer using commands over tasks or a (counter) example when not to?
Try to use tasks whenever you can. They are easier to understand and easier to write.
From the documentation:
Typically, you would resort to a command when you need to do something that’s impossible in a regular task.
That of course doesn't help if it's not specified what the limitations of tasks are. I however personally have taken it to mean: if you want to manipulate the state
of the build, you can use commands.
An example of this is when you want to run a task with different settings.
Tasks give you access to other settings and tasks, commands will also give you access to the state and allow you to manipulate it. That however comes at a cost of unfamiliarity to other developers and increased complexity.
There might be cases where something can be expressed both as a task and a command. Here I'd take the approach of picking the one that feels easier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With