June 10

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().