List comprehensions for D

Quirin Schroll qs.il.paperinik at gmail.com
Mon Jun 10 14:44:42 UTC 2024


### 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:
```d
[ 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:
```d
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.
```d
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`](https://dlang.org/spec/grammar.html#ArgumentList)* `;` *`Comprehensors`* `]`
*`Comprehensors`*:
    *`Comprehensor`* `;`?
    *`Comprehensor`* `;` *`Comprehensors`*?
*`Comprehensor`*:
    *[`AggregateForeach`](https://dlang.org/spec/grammar.html#AggregateForeach)*
    *[`RangeForeach`](https://dlang.org/spec/grammar.html#RangeForeach)*
    `if` `(` 
*[`IfCondition`](https://dlang.org/spec/grammar.html#IfCondition)* `)`
    *[`VarDeclarations`](https://dlang.org/spec/grammar.html#VarDeclarations)*

### Semantics

This is implemented by a lowering. A ComprehensionExpression is 
an object of the following type:
```d
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()`.


More information about the dip.ideas mailing list