March 29, 2013
kenji hara:

> If tup[0..1] makes closed tuple implicitly, you cannot make new flattened
> tuple from other tuples.
>
> auto x = {1,"hi"};
> auto y = {[1,2], S(1)};
> auto tup1 = {x[], y[]};   // creates {1,"hi", [1,2], S(1)}
> auto tup2 = {x, y};   // creates {{1,"hi"}, {[1,2], S(1)}}
>
> Under your semantics, it is impossible.

I (and probably Timon) am just asking for another syntax to do that. Not to make it impossible.

A clean syntax to concatenate tuples:

auto tup3 = x ~ y; // creates {1,"hi", [1,2], S(1)}


In general I agree we need a syntax to apply the tuple items to a function. But I think the slice syntax is the wrong syntax for it.


In Python you use a star, this syntax can't be used in D:

>>> def foo(x, y): pass
...
>>>
>>> t = (1, 2)
>>> foo(*t)


In the Python itertools there is also a map that performs a star too:

>>> from itertools import starmap
>>> pow(2, 3)
8
>>> t = (2, 3)
>>> list(starmap(pow, [t]))
[8]


In Haskell there is more than one way to do that, like using uncurry that creates a new function:

Prelude> mod 10 4
2
Prelude> let t = (10, 4)
Prelude> let mod2 = uncurry mod
Prelude> mod2 t
2
Prelude> uncurry mod (10, 4)
2


Bye,
bearophile
March 29, 2013
kenji hara:

> I think it will become reasonable. If you really want re-packing tuple, you
> can use {tup[0..1]}. It's quite handy.

In the end it's not too much bad (despite my first instinct is for a slice of a tuple to be a true tuple), and so far I have not found better alternatives. Let's see what other people think about that.

Bye,
bearophile
March 29, 2013
On 03/29/2013 05:39 PM, kenji hara wrote:
> 2013/3/30 bearophile <bearophileHUGS@lycos.com
> <mailto:bearophileHUGS@lycos.com>>
>
>     kenji hara:
>
>
>         I think it will become reasonable. If you really want re-packing
>         tuple, you
>         can use {tup[0..1]}. It's quite handy.
>
>
>     This is a bad idea. It is not handy and it introduces a special
>     case. Explicit is better than implicit.
>
>
> That is "explicit".  In D, opened tuple (currently it's generated from
> template parameters, e.g. std.typetuple.TypeTuple) and closed tuple
> (currently created by structs with alias tuple_field this; e.g.
> std.typecons.Tuple) are distinct. Slice operator always returns opened
> tuple.

Because of prior language limitations, not because it makes any sense!

Current operator overloading limitations mandate that it is simply not possible to create a Phobos tuple with a sane slice operator.

> If tup[0..1] makes closed tuple implicitly, you cannot make new
> flattened tuple from other tuples.
>
> auto x = {1,"hi"};
> auto y = {[1,2], S(1)};
> auto tup1 = {x[], y[]};   // creates {1,"hi", [1,2], S(1)}
> auto tup2 = {x, y};   // creates {{1,"hi"}, {[1,2], S(1)}}
>
> Under your semantics, it is impossible.
> ...

Certainly not. Creating opened tuples from closed ones is not an operation necessarily tied to slicing.

Making an opened tuple should be an explicit operation on closed tuples.
Slicing and opening are orthogonal operations.

auto tup1 = {x.open, y.open}; // creates {1,"hi", [1,2], S(1)}
auto tup2 = {x[0..2], y[]}; // creates {{1,"hi}, {[1,2], S(1)}}

auto tup3 = {x[0..2].open, y[].open}; // creates {1,"hi", [1,2], S(1)}
March 29, 2013
On 03/29/2013 09:57 AM, kenji hara wrote:
> http://wiki.dlang.org/DIP32
>
> Kenji Hara

One quite ugly thing about this is that function arguments will be a distinct, incompatible and underpowered form of packing together and unpacking multiple values.

auto foo(int a, int b){ } // unpacking

void main(){
    foo(1,2); // packing
}

Ideal for UFCS would be:

{a,b}.zip.map!({x,y}=>x+y);


It is therefore likely that people will start to write two overloads, where one takes multiple arguments and the other takes one tuple argument.
March 29, 2013
Another reason to allow swapping values ( {a, b} = {b, a} ) is nice parallel semantics that might provide cool features on some future (or niche) parallel hardware.
Think of
{a, b} = {funcA(), funcB()};
Parallel semantics is able to evolve to parallel execution, which is one cool feature.
In Go language it is so.
March 29, 2013
kenji hara:

> http://wiki.dlang.org/DIP32

Regarding case values:

> switch (tup) {
>     case {1, 2}:
>     case {$, 2}:
>     case {1, x}:    // capture tup[1] into 'x' when tup[0] == 1
>     default:        // same as {...}
> }


It's useful to switch on struct values:


import std.bigint;
void main() {
    auto x = BigInt(3);
    switch (x) {
        case BigInt(0): break;
        default: break;
    }
}


Other examples of Phobos structs that is useful to switch on are Nullable, Algebraic, etc.

Switching on structs is more easy if the struct has no ctor. So it's a POD (despite having some other method).

To support the general case of structs that have a constructor such structs need a standard method named like "unapply", that is used by the switch itself. This is the solution used by Scala language:

http://www.scala-lang.org/node/112


This example is in Scala language:


object Twice {
  def apply(x: Int): Int = x * 2
  def unapply(z: Int): Option[Int] = if (z%2 == 0) Some(z/2) else None
}

object TwiceTest extends Application {
  val x = Twice(21)
  x match { case Twice(n) => Console.println(n) } // prints 21
}



It's equivalent to the D code:


import std.stdio;
import std.typecons: Nullable;

struct Twice {
    int static opCall(int x) {
        return x * 2;
    }

    Nullable!int unapply(int z) {
        if (z % 2 == 0)
            return typeof(return)(z / 2);
        else
            return typeof(return).init;
    }
}

void main() {
    immutable int x = Twice(21);
    assert(x == 42);

    switch (x) {
        case Twice(n):
            writeln(n); // prints 21
            break;
        default:
    }
}



A different example:


import std.stdio;
import std.typecons: Nullable;

struct Foo {
    int x;

    this(int x_) {
        this.x = x_ * 2;
    }

    Nullable!int unapply(Foo f1) const {
        return typeof(return)(f1.x / 2);
    }
}

void main() {
    immutable Foo f2 = Foo(10);
    assert(f1.x == 20);

    switch (f2) {
        case Foo(5):
            writeln("First case: 5");
            break;
        case Foo(n):
            writeln(n); // Prints: 10
            break;
        default:
    }
}



A third example:

import std.stdio;

struct Even {
    bool unapply(int x) {
        return x % 2 == 0;
    }
}

void main() {
    int x = 17;

    switch (x) {
        case Even():
            writeln("even");
            break;
        default:
            writeln("odd");
    }
}


unapply() is allowed to return a bool or a Nullable (including a Nullable of a tuple).

For more info:

http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

Bye,
bearophile
March 30, 2013
not 'open', there is already something called .expand property

http://forum.dlang.org/thread/fdkalkzhchuerkqlpzkg@forum.dlang.org

how about this:
auto x = {1,2,3};
auto x2 = {1,2};
assert(x[0..2]==x2);//no expansion
void fun(int x, int y);
fun(x2.expand);
fun(x[0..2].expand);

keeps slicing orthogonal from expansion

> On Fri, Mar 29, 2013 at 10:31 AM, Timon Gehr <timon.gehr@gmx.ch> wrote:
>> On 03/29/2013 05:39 PM, kenji hara wrote:
>>>
>>> 2013/3/30 bearophile <bearophileHUGS@lycos.com <mailto:bearophileHUGS@lycos.com>>
>>>
>>>
>>>     kenji hara:
>>>
>>>
>>>         I think it will become reasonable. If you really want re-packing
>>>         tuple, you
>>>         can use {tup[0..1]}. It's quite handy.
>>>
>>>
>>>     This is a bad idea. It is not handy and it introduces a special
>>>     case. Explicit is better than implicit.
>>>
>>>
>>> That is "explicit".  In D, opened tuple (currently it's generated from template parameters, e.g. std.typetuple.TypeTuple) and closed tuple (currently created by structs with alias tuple_field this; e.g. std.typecons.Tuple) are distinct. Slice operator always returns opened tuple.
>>
>>
>> Because of prior language limitations, not because it makes any sense!
>>
>> Current operator overloading limitations mandate that it is simply not possible to create a Phobos tuple with a sane slice operator.
>>
>>> If tup[0..1] makes closed tuple implicitly, you cannot make new flattened tuple from other tuples.
>>>
>>> auto x = {1,"hi"};
>>> auto y = {[1,2], S(1)};
>>> auto tup1 = {x[], y[]};   // creates {1,"hi", [1,2], S(1)}
>>> auto tup2 = {x, y};   // creates {{1,"hi"}, {[1,2], S(1)}}
>>>
>>> Under your semantics, it is impossible.
>>> ...
>>
>>
>> Certainly not. Creating opened tuples from closed ones is not an operation necessarily tied to slicing.
>>
>> Making an opened tuple should be an explicit operation on closed tuples. Slicing and opening are orthogonal operations.
>>
>> auto tup1 = {x.open, y.open}; // creates {1,"hi", [1,2], S(1)}
>> auto tup2 = {x[0..2], y[]}; // creates {{1,"hi}, {[1,2], S(1)}}
>>
>> auto tup3 = {x[0..2].open, y[].open}; // creates {1,"hi", [1,2], S(1)}
March 30, 2013
On Friday, 29 March 2013 at 12:56:10 UTC, bearophile wrote:
> $ is used for array lengths, so it's not so good to overload it to mean "don't care" too.
>
> Alternative syntaxes:
>
> {c, $_} = tup;
> {c, @} = tup;
> {c, @_} = tup;
> {c, $$} = tup;
> {c, {}} = tup;
> {c, {_}} = tup;
> {c, $~} = tup;
> {c, @~= tup;
> etc.

{c, ?} = tup;

March 30, 2013
Zach the Mystic:

>> {c, $_} = tup;
>> {c, @} = tup;
>> {c, @_} = tup;
>> {c, $$} = tup;
>> {c, {}} = tup;
>> {c, {_}} = tup;
>> {c, $~} = tup;
>> {c, @~= tup;
>> etc.
>
> {c, ?} = tup;

Right, I was forgetting that.
Or this if you want to keep the single "?" for hypothetical future nullable types:

{c, ?_} = tup;

Bye,
bearophile
March 31, 2013
On Friday, 29 March 2013 at 15:47:52 UTC, Timon Gehr wrote:
>> template X(T...) {}
>> alias x = X!(int, long);  // T captures {int, long}
>>
>
> Not really. T captures {int, long}.expand.

Should there be a difference?