Re: non empty slices

2016-06-02 Thread Alex via Digitalmars-d-learn

On Thursday, 2 June 2016 at 23:44:49 UTC, ag0aep6g wrote:

On 06/03/2016 01:35 AM, ag0aep6g wrote:
The alternative `peek` method is not documented to throw an 
exception,

but it's not @nogc either. No idea why. Maybe Algebraic does GC
allocations internally. I wouldn't know for what, though. Or 
it misses a

@nogc somewhere.


I've looked at the source to see if it's something simple, and 
Algebraic/VariantN seems to be terribly complicated. Writing a 
simpler @nogc tagged union may be easier than fixing the phobos 
one, if the phobos one can even be made @nogc.


I'm also inside the source... yes, its not a simple one. I think 
I will try to write my own one...


Re: non empty slices

2016-06-02 Thread Alex via Digitalmars-d-learn

On Thursday, 2 June 2016 at 23:35:53 UTC, ag0aep6g wrote:
It's the Algebraic. The `get` method isn't @nogc. The 
documentation [1] says that it may throw an exception, which is 
most probably being allocated through the GC. So that's a 
reason why it can't be @nogc.


The alternative `peek` method is not documented to throw an 
exception, but it's not @nogc either. No idea why. Maybe 
Algebraic does GC allocations internally. I wouldn't know for 
what, though. Or it misses a @nogc somewhere.



[1] http://dlang.org/phobos/std_variant#.VariantN.get


Yeah... thanks a lot!


Re: non empty slices

2016-06-02 Thread ag0aep6g via Digitalmars-d-learn

On 06/03/2016 01:35 AM, ag0aep6g wrote:

The alternative `peek` method is not documented to throw an exception,
but it's not @nogc either. No idea why. Maybe Algebraic does GC
allocations internally. I wouldn't know for what, though. Or it misses a
@nogc somewhere.


I've looked at the source to see if it's something simple, and 
Algebraic/VariantN seems to be terribly complicated. Writing a simpler 
@nogc tagged union may be easier than fixing the phobos one, if the 
phobos one can even be made @nogc.


Re: non empty slices

2016-06-02 Thread ag0aep6g via Digitalmars-d-learn

On 06/03/2016 01:17 AM, Alex wrote:

But still, I can't mark the f-method @nogc, and this is not due to the
writeln calls... why GC is invoked, although everything is known and no
memory allocation should happen?


It's the Algebraic. The `get` method isn't @nogc. The documentation [1] 
says that it may throw an exception, which is most probably being 
allocated through the GC. So that's a reason why it can't be @nogc.


The alternative `peek` method is not documented to throw an exception, 
but it's not @nogc either. No idea why. Maybe Algebraic does GC 
allocations internally. I wouldn't know for what, though. Or it misses a 
@nogc somewhere.



[1] http://dlang.org/phobos/std_variant#.VariantN.get


Re: non empty slices

2016-06-02 Thread Alex via Digitalmars-d-learn

On Thursday, 2 June 2016 at 22:17:32 UTC, ag0aep6g wrote:


Yeah, can't do it that way. You have only one f_impl call, but 
want it to go to different overloads based on dynamic 
information (caseS). That doesn't work.


You need three different f_impl calls. You can generate them, 
so there's only one in the source, but it's a bit involved:


sw: switch (caseS)
{
foreach (i, T; TL)
{
case i: f_impl(result.get!T); break sw;
}
default: assert(false);
}

Oh... wow... cool! :)
But still, I can't mark the f-method @nogc, and this is not due 
to the writeln calls... why GC is invoked, although everything is 
known and no memory allocation should happen?


Re: non empty slices

2016-06-02 Thread ag0aep6g via Digitalmars-d-learn

On 06/02/2016 11:37 PM, Alex wrote:

Just tried this instead of your f-function:
void f(int[] arr)
{
 A result;
 import std.meta;
 alias TL = AliasSeq!(Empty, int, Many!int);
 int caseS;
 switch (arr.length)
 {
 case 0: result = Empty.init; caseS = 0; break;
 case 1: result = arr[0]; caseS = 1;  break;
 default: result = Many!int(arr); caseS = 2;
 }
 f_impl(*result.get!(TL[caseS]));
}
But got: Error: variable caseS cannot be read at compile time
which is obviously true...


Yeah, can't do it that way. You have only one f_impl call, but want it 
to go to different overloads based on dynamic information (caseS). That 
doesn't work.


You need three different f_impl calls. You can generate them, so there's 
only one in the source, but it's a bit involved:


sw: switch (caseS)
{
foreach (i, T; TL)
{
case i: f_impl(result.get!T); break sw;
}
default: assert(false);
}



Re: non empty slices

2016-06-02 Thread ag0aep6g via Digitalmars-d-learn

On 06/02/2016 10:11 PM, Alex wrote:

The cool thing about the Algebraic is as I expected, that it doesn't
change it's type... And the hard thing is, that I'm not used to its
Empty, Many, ... things yet.


I just made those up on the spot. Note that Many is not actually 
implemented at all. There is no check that the array has at least two 
elements. And Empty is just there, because I needed a type for the 
Algebraic.



But the question remains how to keep this @nogc?


Apparently, it's Algebraic that isn't @nogc. I don't know what it 
allocates. Maybe it allocates space for large types (but there aren't 
any here), or maybe it can throw a GC-allocated exception.



I wonder at the line
with peek... and why it is not just returning the value...


I wouldn't expect that to be the problem with @nogc. As far as I see, 
the pointer is used as a way to return "not found" in the form of null. 
When you get a non-null pointer it's probably just into the Algebraic.


Re: non empty slices

2016-06-02 Thread Alex via Digitalmars-d-learn

On Thursday, 2 June 2016 at 20:11:21 UTC, Alex wrote:

On Thursday, 2 June 2016 at 16:21:03 UTC, ag0aep6g wrote:

void f(int[] arr)
{
A a = arrayToA(arr);
foreach (T; A.AllowedTypes)
{
if (T* p = a.peek!T) f_impl(*p);
}
}

You totally hit the point!
The cool thing about the Algebraic is as I expected, that it 
doesn't change it's type... And the hard thing is, that I'm not 
used to its Empty, Many, ... things yet.
But the question remains how to keep this @nogc? I wonder at 
the line with peek... and why it is not just returning the 
value...


Just tried this instead of your f-function:
void f(int[] arr)
{
A result;
import std.meta;
alias TL = AliasSeq!(Empty, int, Many!int);
int caseS;
switch (arr.length)
{
case 0: result = Empty.init; caseS = 0; break;
case 1: result = arr[0]; caseS = 1;  break;
default: result = Many!int(arr); caseS = 2;
}
f_impl(*result.get!(TL[caseS]));
}
But got: Error: variable caseS cannot be read at compile time
which is obviously true...


Re: non empty slices

2016-06-02 Thread Alex via Digitalmars-d-learn

On Thursday, 2 June 2016 at 16:21:03 UTC, ag0aep6g wrote:

On 06/02/2016 05:16 PM, Alex wrote:

I may be getting what you're up to. Maybe not.

So we start with something like this (using arrays instead of 
arbitrary ranges to keep things simple):



import std.stdio: writeln;
void main()
{
f([]);
f([1]);
f([1, 2]);
}
void f(int[] a)
{
if (a.length == 0) f_none(a);
else if (a.length == 1) f_one(a);
else f_many(a);
}
void f_none(int[] a)
{
writeln("nothing to see here");
}
void f_one(int[] a)
{
assert(a.length == 1);
writeln("exactly one: ", a[0]);
}
void f_many(int[] a)
{
assert(a.length >= 2);
writeln("at least two: ", a[0 .. 2]);
}


And instead you'd like something more like this:


import std.stdio: writeln;
import std.variant: Algebraic;

void main()
{
f([]);
f([1]);
f([1, 2]);
}

void f(int[] arr)
{
A a = arrayToA(arr);
foreach (T; A.AllowedTypes)
{
if (T* p = a.peek!T) f_impl(*p);
}
}

void f_impl(Empty e) { writeln("nothing to see here"); }
void f_impl(int a) { writeln("exactly one: ", a); }
void f_impl(Many!int a) { writeln("at least two: ", a[0 .. 
2]); }


struct Empty {}
struct Many(T)
{
T[] arr;
alias arr this;
/* Somehow it's enforced here that arr.length >= 2. */
}

alias A = Algebraic!(Empty, int, Many!int);
A arrayToA(int[] a)
{
A result;
switch (a.length)
{
case 0: result = Empty.init; break;
case 1: result = a[0]; break;
default: result = Many!int(a);
}
return result;
}


And the advantages of it are:
* Don't need asserts in the different `f_impl`s.

Yes.

* The branching in f is guided by the parameter types of f_impl 
(could possibly be extracted, instead of being hardcoded 
manually).

Yes! :)



Tell me if I'm way off.

You totally hit the point!
The cool thing about the Algebraic is as I expected, that it 
doesn't change it's type... And the hard thing is, that I'm not 
used to its Empty, Many, ... things yet.
But the question remains how to keep this @nogc? I wonder at the 
line with peek... and why it is not just returning the value...


Re: non empty slices

2016-06-02 Thread ag0aep6g via Digitalmars-d-learn

On 06/02/2016 05:16 PM, Alex wrote:

What I mean is: if there would be a possibility to use algebraic types
here (or maybe some kind of template...), then my types would be able
(maybe! did not seen anything similar yet...) to choose the proper
methods for themselves automatically:
As long as my type has a length > 1 it is handled as a range, if the
range has the length=1 it is handled as a single element and not as a
range, if the range has the length=0 it is handled as an absent element.


I may be getting what you're up to. Maybe not.

So we start with something like this (using arrays instead of arbitrary 
ranges to keep things simple):



import std.stdio: writeln;
void main()
{
f([]);
f([1]);
f([1, 2]);
}
void f(int[] a)
{
if (a.length == 0) f_none(a);
else if (a.length == 1) f_one(a);
else f_many(a);
}
void f_none(int[] a)
{
writeln("nothing to see here");
}
void f_one(int[] a)
{
assert(a.length == 1);
writeln("exactly one: ", a[0]);
}
void f_many(int[] a)
{
assert(a.length >= 2);
writeln("at least two: ", a[0 .. 2]);
}


And instead you'd like something more like this:


import std.stdio: writeln;
import std.variant: Algebraic;

void main()
{
f([]);
f([1]);
f([1, 2]);
}

void f(int[] arr)
{
A a = arrayToA(arr);
foreach (T; A.AllowedTypes)
{
if (T* p = a.peek!T) f_impl(*p);
}
}

void f_impl(Empty e) { writeln("nothing to see here"); }
void f_impl(int a) { writeln("exactly one: ", a); }
void f_impl(Many!int a) { writeln("at least two: ", a[0 .. 2]); }

struct Empty {}
struct Many(T)
{
T[] arr;
alias arr this;
/* Somehow it's enforced here that arr.length >= 2. */
}

alias A = Algebraic!(Empty, int, Many!int);
A arrayToA(int[] a)
{
A result;
switch (a.length)
{
case 0: result = Empty.init; break;
case 1: result = a[0]; break;
default: result = Many!int(a);
}
return result;
}


And the advantages of it are:
* Don't need asserts in the different `f_impl`s.
* The branching in f is guided by the parameter types of f_impl (could 
possibly be extracted, instead of being hardcoded manually).


Tell me if I'm way off.


Re: non empty slices

2016-06-02 Thread Alex via Digitalmars-d-learn

On Thursday, 2 June 2016 at 14:31:15 UTC, ag0aep6g wrote:
A little terminology: "Slice" is not a synonym for "range". 
iota does not return a slice, it returns a range. "Slice" is 
being used as a synonym for "dynamic array" (Type[]), and in 
the context of the slicing operator (expression[] or 
expression[a .. b]).


Now, for the actual question - how to define a range that is 
non-empty by definition:


One of the range primitives is popFront. popFront removes one 
element from the range. popFront cannot change the type of the 
range. So you can't have a type that is a range, and is 
guaranteed not to be empty, and can be emptied by calling 
popFront.


But you can have a type that is a range, is guaranteed not to 
be empty, and *cannot* be emptied by calling popFront: an 
infinite range. That's a range that never gets empty no matter 
how often popFront is called.


The library recognizes infinite ranges when you define `empty` 
like this: `enum bool empty = false;`. There's 
std.range.primitives.isInfinite [1] to check for them.


I guess that's not really what you're looking for.


Yes, that's not. :)

I don't think you can otherwise statically check that a range 
isn't empty. You could probably make a wrapper that checks once 
on construction, but that's still a run-time check. And I don't 
see it being particularly useful, because popFront is an 
essential parts of ranges.


So this would mean, the contract I use now for run-time check is 
ok.
And... well... maybe I can alias two different methods, one which 
returns an arbitrary range and one which returns a range, checked 
for emptiness in an out contract...

This already would save a lot of code duplication...

You've lost me there. I don't see how non-empty range and 
Algebraic connect.


What I mean is: if there would be a possibility to use algebraic 
types here (or maybe some kind of template...), then my types 
would be able (maybe! did not seen anything similar yet...) to 
choose the proper methods for themselves automatically:
As long as my type has a length > 1 it is handled as a range, if 
the range has the length=1 it is handled as a single element and 
not as a range, if the range has the length=0 it is handled as an 
absent element.


Re: non empty slices

2016-06-02 Thread ag0aep6g via Digitalmars-d-learn

On 06/02/2016 03:37 PM, Alex wrote:

The question is, how to define the same thing for ranges. What I started
with is:

import std.range : iota;
alias MrSlice = typeof(iota(M.init));

so far, so good. The MrSlice-thing is the analogy for Mr, as it can be
empty, as Mr can.
What I also need is the analogy for the M-thing. But how can I define a
slice, which is not-empty by definition?


A little terminology: "Slice" is not a synonym for "range". iota does 
not return a slice, it returns a range. "Slice" is being used as a 
synonym for "dynamic array" (Type[]), and in the context of the slicing 
operator (expression[] or expression[a .. b]).


Now, for the actual question - how to define a range that is non-empty 
by definition:


One of the range primitives is popFront. popFront removes one element 
from the range. popFront cannot change the type of the range. So you 
can't have a type that is a range, and is guaranteed not to be empty, 
and can be emptied by calling popFront.


But you can have a type that is a range, is guaranteed not to be empty, 
and *cannot* be emptied by calling popFront: an infinite range. That's a 
range that never gets empty no matter how often popFront is called.


The library recognizes infinite ranges when you define `empty` like 
this: `enum bool empty = false;`. There's 
std.range.primitives.isInfinite [1] to check for them.


I guess that's not really what you're looking for. I don't think you can 
otherwise statically check that a range isn't empty. You could probably 
make a wrapper that checks once on construction, but that's still a 
run-time check. And I don't see it being particularly useful, because 
popFront is an essential parts of ranges.


[...]

I tried also something using Algebraic types with an underlying int, but
couldn't manage to append elements to something like:
alias List2 = Algebraic!(This[]);
or
alias List(Leaf) = Algebraic!(Leaf, This*);

The cool thing is, if the algebraic stuff works, it would potentially be
able to replace all four aliases altogether...
But a knock-out criteria is: the solution has to be @nogc


You've lost me there. I don't see how non-empty range and Algebraic connect.


[1] https://dlang.org/phobos/std_range_primitives.html#.isInfinite