Intro
Many languages, especially high-level and functional ones, have list comprehensions. Translated to D lingo, one can get a range object with elements based on other ranges. The hardest thing for D is to come up with a reasonable syntax and semantics.
Comprehensions have two main advantages:
- They are more readable than filter and map operations.
- They are more concise than filter and map operations.
Design
Generally speaking, a comprehension has two parts: destinations and sources/conditions/variables.
So, the basic template is this:
[ ArgumentList ; Comprehensors ]
The argument list usually has 1 entry, but it can have more.
The Comprehensors
are what I called sources/conditions/variables. They are semicolon-separated and every Comprehensor
is of the form of a foreach
(for a source), an if
(for a condition), or a variable declaration.
Simple Example
A simple example of a filter:
foreach (x; [ x; foreach (x; xs); if (x > 0) ]) { … }
The bracketed thing is a first-class object and has a type (unspelled, like a lambda), so it can be e.g. assigned to a local variable.
auto positives = [ x; foreach (x; xs); if (x > 0) ];
foreach (x; positives) { … }
Grammar
Without links:
ComprehensionExpression:
[ ArgumentList ; Comprehensors ]
Comprehensors:
Comprehensor ;?
Comprehensor ; Comprehensors?
Comprehensor:
AggregateForeach
RangeForeach
if ( IfCondition )
VarDeclarations
With Links:
ComprehensionExpression
:
[
ArgumentList
;
Comprehensors
]
Comprehensors
:
Comprehensor
;
?
Comprehensor
;
Comprehensors
?
Comprehensor
:
AggregateForeach
RangeForeach
if
(
IfCondition
)
VarDeclarations
Semantics
This is implemented by a lowering. A ComprehensionExpression is an object of the following type:
struct CE // name is exposition-only and an implementation defined name
{
int opApplyImpl(DG)(scope DG dg) // name is exposition-only and an implementation defined name
{
// Comprehensors
if (auto result = dg(ArgumentList)) return result;
return 0;
}
alias opApply = opApplyImpl!(int delegate(
typeof(ArgumentList) // (see comment below)
));
}
For arguments that are lvalues, a ref
is included unless the argument is a non-ref
local variable (or foreach variable) declared in the comprehension expression.
The ComprehensionExpression
is replaced by CE()
.