Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

Uniform tuple syntax

Version 1
Created 2013-03-29
StatusDraft
Last modified 2013-03-29
Author Hara Kenji

Abstract

This is a proposal for consistent tuple syntax and features.

Generic type/expression tuple syntax

Use braces and commas. Inside tuple literal, ; never appears. So it will not be confused with lambdas and ScopeStatements.

auto tup = {10, "hi", 3.14};
assert(tup[0] == 10);   // indexing access

// In initializers
auto fp = {;};  // lambda
auto dg = {x;}; // lambda
auto tup = {};  // zero-element tuple (Syntax meaning will be changed!)
Struct s = {};  // StructInitializer

// In function arguments
foo({;});       // lambda
foo({});        // zero-element tuple (Syntax meaning will be changed!)
foo({1});       // one-element tuple
foo({1, "hi"}); // two-element tuple

// In statements
void test() {
    {1, "hi"}   // two-element tuple
    {1}         // one-element tuple
    {}          // ScopeStatement with no statements, not zero-element tuple.
    // {} = tup;  // no-side effect assignment.
    // {} = tup;  // meaningless unpacking & declaration.
    // if (true) {} func;
                  // ScopeStatement and parenthesis-less func call.
}

// declare tuple value by using explicit tuple type
{int, string} tup = {1, "hi"};
assert(tup[0] == 1);
assert(tup[1] == "hi");
static assert(is(typeof(tup) == {int, string}));    // tuple type

alias TL = {int, string[], double[string]};  // types
alias Fields = {int, "num", int[string], "map"};  // mixing
alias date = {2013, 3, 29};  // values

foreach (Float; {float, double, real}) { ... }

Tuple unpacking and pattern matching

Tuple value unpacking will become handy with help of language syntax. Also, pattern matching syntax will be allowed in some places.

Various places that you can use unpacking

Various places that you can use unpacking and pattern matching

Difference between unpacking and pattern matching

Pattern matching is only allowed in if and case statements.

auto coord = {1, 2};
if (auto {1, y} = coord) {}         // if statement, ok
switch(coord) { case {1, y}: ...; } // case statement, ok
auto {1, y} = coord;                // variable declaration, ng!

Because the two have conditional statements which is evaluated iff the pattern matches to the operand. Therefore $identifier is only allowed for the pattern match.

`...` meaning for unpacking
`...` is used for the special unpacking placeholder. It matches zero or more elements. ```d auto tup = {1, "hi", 3.14, [1,2,3]}; if (auto {1, "hi", ...} = tup) {} ```
`$` meaning for unpacking
**Comment: Martin Nowak** I'd suggest to use `_` as a general placeholder for unused variables (see [Issue 13522](https://issues.dlang.org/show_bug.cgi?id=13522)). There is precedence of using `_` in other languages (python, haskell, scala) and it's useful outside of tuple unpacking. `$` is used for the special unpacking placeholder for one element but not used. It does not conflict with curent `array.length` usage, because: 1. all pattern matching is always appeared in statements. 2. To use statements in array indices, function literal is necessary. But: ```d int[] a = [1,2,3]; auto x = a[(){ return $-1; }()]; // Error: cannnot use $ inside a function literal ``` Therefore, ```d auto x = a[(){ if (auto {1, $} = tup) { // '$' is always *pattern placeholder* ... } return 0; }]; ```
`$identifier` meaning for pattern matching
\$identifier is used for the special syntax for pattern matching. Inside pattern, bare `identifier` will always make placeholder. ```d if (auto {x, y} = coord) { // x and y captures coord's elements only in 'then' statement. } ``` If newly declared valiable conflicts with outer variables, it is refused. ```d int x = 1; if (auto {x, y} = coord) { auto x2 = x; } // error, ambiguous 'x' usage ``` If you want to make a pattern with evaluating variable, use `$identifier`. ```d int x = 1; if (auto {$x, y} = coord) { ... } // If the first element of coord is equal to 1 (== x), 'then' statement wil be evaluated. ``` ### Unpacking and tuple expansion Unpacking implicitly requires tuple for its operand, so it can be automatically expanded. ```d auto coord = {1, 2}; if (auto {x, y} = coord) {} if (auto {x, y} = coord[]) {} // same, explicitly expands fields ``` ### Mismatching tuple element types and length ```d auto tup = {1, "hi"} if (auto {num} = tup) {} // compile error if (auto {num, msg, x} = tup) {} // compile error if ({string num, int msg} = tup) {} // compile error if (auto {num, msg, ...} = tup) {} // ok, `...` matches to zero-elements. ``` Use case of uniform tuple syntax -------------------------------- Original D code: Source: <http://forum.dlang.org/post/gridjorxqlpoytuxwpsg@forum.dlang.org> ```d import std.stdio, std.algorithm, std.typecons, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(s => tuple(s[1], [tuple(s[0], "")])) .array.heapify!q{b < a}; while (heap.length > 1) { auto lo = heap.front; heap.removeFront; auto hi = heap.front; heap.removeFront; foreach (ref pair; lo[1]) pair[1] = '0' ~ pair[1]; foreach (ref pair; hi[1]) pair[1] = '1' ~ pair[1]; heap.insert(tuple(lo[0] + hi[0], lo[1] ~ hi[1])); } return heap.front[1].schwartzSort!q{tuple(a[1].length, a[0])}; } void main() { auto s = "this is an example for huffman encoding"d; foreach (p; s.dup.sort().release.group.encode) writefln("'%s' %s", p[]); } ``` This proposal: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!({c, f} => {f, [{c, ""}]}).array.heapify!q{b < a}; while (heap.length > 1) { auto {lof, loa} = heap.front; heap.removeFront; auto {hif, hia} = heap.front; heap.removeFront; foreach ({$, ref e}; loa) e = '0' ~ e; foreach ({$, ref e}; hia) e = '1' ~ e; heap.insert({lof + hif, loa ~ hia}); } return heap.front[1].schwartzSort!({c, e} => {e.length, c}); } void main() { auto s = "this is an example for huffman encoding"d; foreach ({c, e}; s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` Basic () syntax, perhaps the cleanest, but can't be used: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!((c, f) => (f, [(c, "")])).array.heapify!q{b < a}; while (heap.length > 1) { auto (lof, loa) = heap.front; heap.removeFront; auto (hif, hia) = heap.front; heap.removeFront; foreach ((_, ref e); loa) e = '0' ~ e; foreach ((_, ref e); hia) e = '1' ~ e; heap.insert((lof + hif, loa ~ hia)); } return heap.front[1].schwartzSort!((c, e) => (e.length, c)); } void main() { auto s = "this is an example for huffman encoding"d; foreach ((c, e); s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` tuple() syntax, clear, a bit long: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(tuple(c, f) => tuple(f, [tuple(c, "")])) .array.heapify!q{b < a}; while (heap.length > 1) { auto tuple(lof, loa) = heap.front; heap.removeFront; auto tuple(hif, hia) = heap.front; heap.removeFront; foreach (tuple(_, ref e); loa) e = '0' ~ e; foreach (tuple(_, ref e); hia) e = '1' ~ e; heap.insert(tuple(lof + hif, loa ~ hia)); } return heap.front[1].schwartzSort!(tuple(c, e) => tuple(e.length, c)); } void main() { auto s = "this is an example for huffman encoding"d; foreach (tuple(c, e); s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` @{} syntax, noisy: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(@{c, f} => {f, [@{c, ""}]}).array.heapify!q{b < a}; while (heap.length > 1) { auto @{lof, loa} = heap.front; heap.removeFront; auto @{hif, hia} = heap.front; heap.removeFront; foreach (@{_, ref e}; loa) e = '0' ~ e; foreach (@{_, ref e}; hia) e = '1' ~ e; heap.insert(@{lof + hif, loa ~ hia}); } return heap.front[1].schwartzSort!(@{c, e} => @{e.length, c}); } void main() { auto s = "this is an example for huffman encoding"d; foreach (@{c, e}; s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` (||) banana syntax, a bit confusing, but IDEs can visualize (| and |) as some nice Unicode glyps: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!((|c, f|) => (|f, [(|c, ""|)]|)).array.heapify!q{b < a}; while (heap.length > 1) { auto (|lof, loa|) = heap.front; heap.removeFront; auto (|hif, hia|) = heap.front; heap.removeFront; foreach ((|_, ref e|); loa) e = '0' ~ e; foreach ((|_, ref e|); hia) e = '1' ~ e; heap.insert((|lof + hif, loa ~ hia|)); } return heap.front[1].schwartzSort!((|c, e|) => (|e.length, c|)); } void main() { auto s = "this is an example for huffman encoding"d; foreach ((|c, e|); s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` Something like: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(⦅c, f⦆ => ⦅f, [⦅c, ""⦆]⦆).array.heapify!q{b < a}; while (heap.length > 1) { auto ⦅lof, loa⦆ = heap.front; heap.removeFront; auto ⦅hif, hia⦆ = heap.front; heap.removeFront; foreach (⦅_, ref e⦆; loa) e = '0' ~ e; foreach (⦅_, ref e⦆; hia) e = '1' ~ e; heap.insert(⦅lof + hif, loa ~ hia⦆); } return heap.front[1].schwartzSort!(⦅c, e⦆ => ⦅e.length, c⦆); } void main() { auto s = "this is an example for huffman encoding"d; foreach (⦅c, e⦆; s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` t{} syntax, the t is not very easy to see, but it's short and it's similar to the q{} for token strings: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(t{c, f} => {f, [t{c, ""}]}).array.heapify!q{b < a}; while (heap.length > 1) { auto t{lof, loa} = heap.front; heap.removeFront; auto t{hif, hia} = heap.front; heap.removeFront; foreach (t{_, ref e}; loa) e = '0' ~ e; foreach (t{_, ref e}; hia) e = '1' ~ e; heap.insert(t{lof + hif, loa ~ hia}); } return heap.front[1].schwartzSort!(t{c, e} => t{e.length, c}); } void main() { auto s = "this is an example for huffman encoding"d; foreach (t{c, e}; s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` `#()` syntax (suggested by Meta), short, not too much noisy, and it's visually searchable and popping more than t{}: ```d import std.stdio, std.algorithm, std.container, std.array; auto encode(T)(Group!("a == b", T[]) sf) { auto heap = sf.map!(#(c, f) => #(f, [#(c, "")])).array.heapify!q{b < a}; while (heap.length > 1) { auto #(lof, loa) = heap.front; heap.removeFront; auto #(hif, hia) = heap.front; heap.removeFront; foreach (#(_, ref e); loa) e = '0' ~ e; foreach (#(_, ref e); hia) e = '1' ~ e; heap.insert(#(lof + hif, loa ~ hia)); } return heap.front[1].schwartzSort!(#(c, e) => #(e.length, c)); } void main() { auto s = "this is an example for huffman encoding"d; foreach (#(c, e); s.dup.sort().release.group.encode) writefln("'%s' %s", c, e); } ``` Copyright --------- This document has been placed in the Public Domain.