From D Wiki
Revision as of 05:52, 17 March 2014 by Mmcgill (talk | contribs) (Created page with "{| class="wikitable" !Title: !'''".." as a Binary Operator''' |- |DIP: |58 |- |Version: |1 |- |Status: |Draft |- |Created: |2014-03-16 |- |Last Modified: |2014-03-16 |- |Autho...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Title: ".." as a Binary Operator
DIP: 58
Version: 1
Status: Draft
Created: 2014-03-16
Last Modified: 2014-03-16
Author: Mason McGill


Making ".." a binary operator allows multidimensional indexing, terse range construction, and other operations especially useful for scientific and engineering applications, while reducing language complexity and preserving backwards compatibility.


One of D's design goals is to "cater to the needs of numerical analysis programmers". To that end, D already has some syntax to support MATLAB/Python/Julia/R-style multidimensional array manipulation: IndexExpression, SliceExpression, and "$". However, the ability to tersely express a numeric range (e.g. to define function domains and perform multidimensional array slicing) arguably makes languages like Julia more usable for "vectorized" numerical programming than D. Making ".." a general-purpose binary operator would enable this functionality, and improve the usability of numerical APIs.



SliceExpression and ForeachRangeStatement are removed from the language. CaseRangeStatement remains unchanged.

Code in the form "Expression1 .. Expression2" is parsed as a ToExpression. ".." has the lowest operator precedence of any binary operator.

Resolving ToExpression

A ToExpression is delegated to the arguments if they implement "opBinary" or "opBinaryLeft" for the ".." operator. Otherwise, it yields a structure that can be used in an IndexExpression or ForeachStatement to mimic a SliceExpression or ForeachRangeExpression.

auto __evaluateToExpression(Left, Right)(Left left, Right right)
    static if (__traits(compiles, left.opBinary!".."(right)))
        return left.opBinary!".."(right);
    else static if (__traits(compiles, right.opBinaryRight!".."(left)))
        return right.opBinaryRight!".."(left);
        return BoundedRange!(Left, Right)(left, right);

struct BoundedRange(Front, Back)
    Front front;
    Back back;

    static if (is(typeof(front >= back) : bool))
        bool empty() { return front >= back; }

    static if (__traits(compiles, front++))
        void popFront() { front++; }

    /* Other range operations, if supported. */

This behavior can be changed in a non-backwards-compatible release to make ".." behave analogously to other binary operators (refusing to compile if a matching "opBinary" or "opBinaryRight" isn't defined).

Changes to the resolution of IndexExpression

An IndexExpression that would previously have been parsed as a SliceExpression first attempt to delegate to "opSlice". Failing that, they delegate to "opIndex". Index-assignment, index-op-assignment, and index-unary operations are handled analogously. This is similar to the route Python took when changing its indexing/slicing semantics.

auto __evaluateIndexExpression(Base, Indices...)(Base base, Indices indices)
    enum is0ArgSlice = !indices.length;
    enum is2ArgSlice = is(indices[0] == BoundedRange!(F, B), F, B);

    static if (is2ArgSlice)
        auto front = indices[0].front, back = indices[0].back;

    static if (is0ArgSlice && __traits(compiles, base.opSlice())
        return base.opSlice();
    else static if (is2ArgSlice && __traits(compiles, base.opSlice(front, back))
        return base.opSlice(front, back);
        return base.opIndex(indices);

These changes can be reverted in a non-backwards-compatible release to drop support for the "opSlice*" family of functions.


Multidimensonal Slicing

const submatrix = matrix[0..5, 1..3];
matrix[0..5, 1..3] *= 2;

Strided Slicing/Iteration

const oddEntries = vector[(0..10).by(2)]; // Either spelling could be
const evenEntries = vector[1..2..11];     // supported by a library.

writeln(array(0..2..10)); // Prints [0, 2, 4, 6, 8].

Terse Constant Declaration

enum size = [150.cm .. 200.cm, 25.cm .. 50.cm];
enum orientation = -30.deg .. 30.deg;

detectPedestrians(size, orientation);

Defining Multidimensional Grids

const inputSpace = meshgrid(0..100, 0..100);
plot(inputSpace, someFunction(inputSpace));

Simulating Physics

const line = Point(1, 2, 3)..Point(3, 2, 1);
const bounds = Box(1..3, 2..4, 3..6);
const collision = bounds.contains(line);

Parsing Text

const letters = 'a'..'z';
const numbers = '0'..'9';

find(letters ~ numbers, userInput);

Handling Dates/Times

const vacation = july(10, 2014)..july(15, 2014);

foreach (day; monday..friday)


This document has been placed in the Public Domain.