On Friday, 26 May 2017 at 09:59:26 UTC, zakk wrote:
My questions are:
1) Why is D making using of the binary ! operator, which as far
as I understand introduces a template?
The ! operator *instantiates* a template. Whenever you need to
specify compile-time arguments to match the template parameters,
you need to use !. Templated functions are implemented like this:
template myTemplateFunc(<Template parameters go here>) {
void myTemplateFunc(<Function parameters go here>) {
....
}
}
Template parameters are used at compile time, usually to generate
the code for the function, and function parameters are used at
runtime. When the template and the function have the same name,
you can shorten the declaration like so:
void myTemplateFunc(<Template parameters>)(<Function parameters>);
With many templated functions, the template parameters can be
inferred by the compiler from the function parameters:
void myFunc(T)(T arg) {...}
In this case, you can call myFunc using the instantiation
operator:
myFunc!(int)(10);
Or, since the compiler has enough information to infer T (10 is
an int, arg is a T, therefore T is int), you can call it like a
normal function:
myFunc(10);
Template parameters can be given default values, just like
function parameters. If all of the template parameters have
default values or can be inferred, the ! can be omitted in the
function call.
If you look at the documentation for sort [1], you'll see it's
declared to take three template arguments. The first two are
given default values. The last one is not, but since it is the
type of the function parameter, it can be inferred and not
specified. So sort *can* be called like this:
sort(myList);
And this will use the default comparison function, the default
SwapStrategy, and the type of myList will be inferred. If you
want to change the default comparison function or SwapStrategy,
you need to use the instantiation operator to specify them.
That's why it's needed in your example.
2) Why is a template needed here?
It's not that a template is needed, but that sort is implemented
as a templated function. I think my answer to number 1 should
clear this one up, too.
3) It seems to me like the argument passed to the template is a
lambda expression. I only know about templates taking types as
argument. What's going on?
There are a few different kinds of template parameters [2]. Types
are perhaps the most common, but there are also template value
parameters (any compile-time constant), template alias parameters
(which can be any symbol or compile-time constant), and more. In
the declaration of sort, you can see three kinds in action:
SortedRange!(Range, less)
sort(alias less = "a < b", SwapStrategy ss =
SwapStrategy.unstable, Range)(Range r)
The first template parameter, less, is an alias parameter. The
default value is a string literal which can be mixed in. But you
could also pass a function name, a function literal (lambda), or
even an object with a compatible opCall implementation (assuming
the symbol is in the template's scope). The second parameter is a
value parameter. SwapStrategy is an enum, the values of which are
known at compile time. Finally, Range is a type. We know this
because there's no "alias" or type name in front of it -- it's
just a solitary symbol.
[1] http://dlang.org/phobos/std_algorithm_sorting.html#sort
[2] http://dlang.org/spec/template.html