Re: sumtype 0.5.0

2018-08-22 Thread Paul Backus via Digitalmars-d-announce

On Wednesday, 8 August 2018 at 20:54:13 UTC, Paul Backus wrote:
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


New point release, 0.5.3, with the following updates:
- SumType now uses the smallest possible integer type for its tag 
(e.g., `ubyte` if the number of types is less than 255).
- A bug involving structs with invalid .init values has been 
fixed.


Re: sumtype 0.5.0

2018-08-10 Thread Paul Backus via Digitalmars-d-announce

On Friday, 10 August 2018 at 21:28:40 UTC, Everlast wrote:

Yes, but


alias Z = SumType.Union(X,Y);


is not the same as


alias Z = SumType!(int, float, string, complex, vector).


In the first case Z is actually a union of 2 types while in the 
second it is of 5. There is a subtle difference in that in the 
second case the types lose relation. E.g., there is no way to 
recover X or Y from Z but in the first we can:


[...]

ZZ is flat while Z is hierarchical.


If you want to nest SumTypes, there's nothing stopping you from 
doing that already. `alias Z = SumType!(X, Y);` will work just 
fine. I wouldn't necessarily recommend it, since each layer 
introduces additional memory overhead to store the type tag, but 
it will work.


I'm not sure how SumType deals with type info, if it is local 
or global. If it were global, then Z would definitely be 
different than ZZ.


I assume by type info, you mean the tag values? If so, they're 
local to each instantiation of SumType. For example, in 
SumType!(A, B), a tag value of `0` corresponds to the type `A`, 
whereas in SumType!(C, D), the same tag value of `0` corresponds 
to the type `C`.


alias Z = SumType!(X,Y) is a type itself and effectively 
inherits from X and Y but this relationship is not expressed in 
any meaningful way in SumType.


I think we may each have a slightly different idea of what 
"inheritance" means, in this context. When you say "Z inherits 
from X and Y", I interpret that as meaning "Z is a subtype of X 
and Y"--which isn't true. A subtype should obey the 
substitutability principle: anywhere I can use an object of the 
supertype, I should be able to substitute an object of the 
subtype, and my program should still be correct when I do.


But suppose I have a function that accepts an X, and I pass it a 
Z instead. What happens if it turns out, at runtime, that the Z 
contains a Y? The function won't be prepared to handle it--in 
other words, my program is now incorrect. That means the 
substitution was invalid, and Z is not a subtype of X after all.


Now, the relationship *does* hold in the opposite direction: if I 
have a function that accepts a Z, I can pass it an X, and it will 
be able to handle it. Granted, I'll have to do a little bit of 
extra typing to wrap the X (i.e., `f(Z(x))` rather than just 
`f(x)`), because D doesn't allow user-defined implicit 
conversions, but that's not a terribly large burden. (And if I 
really want to write `f(x)`, I can always define an overload of 
`f` that takes an `X` argument and forwards to the `Z` version.)


Maybe SumType!(X,Y) could return a new type that is a class 
that inherits from X and Y? (unfortunately this can't work 
because of single inheritance but these types could probably be 
wrapped in interfaces and properties could be used)


It sounds like I may not have been clear enough about what 
SumType's goal actually is. SumType's goal is not, and has never 
been, to be a feature-complete alternative to OOP language 
features like classes, interfaces, and inheritance. Those 
features already exist in D; there's no need to duplicate them.


SumType's goal is, instead, to provide a much more limited, much 
less flexible version of polymorphism--one that requires every 
possible "subclass" (i.e., member type) and every "method 
override" (i.e., match handler) to be fixed up-front, in one 
place, at compile time. You cannot add new member types to an 
existing SumType, the way you can add new derived classes to an 
existing base class--and this is by design!


Why is SumType designed this way? Because it turns out that this 
very limited version of polymorphism can be implemented *much* 
more efficiently than the real thing. It doesn't require virtual 
methods, it doesn't require heap allocation, and it doesn't 
require runtime type information. In fact, the only thing keeping 
SumType from being BetterC compatible right now is the fact that 
it uses DRuntime features *during CTFE*!


Anyway, I hope I've explained things clearly enough now to avoid 
further misunderstanding. Please let me know if you have any more 
questions or ideas--I really appreciate the thought you've put 
into this discussion.


Re: sumtype 0.5.0

2018-08-10 Thread Everlast via Digitalmars-d-announce

On Friday, 10 August 2018 at 19:19:39 UTC, Paul Backus wrote:

On Friday, 10 August 2018 at 13:11:13 UTC, Everlast wrote:

On Friday, 10 August 2018 at 12:35:18 UTC, Everlast wrote:


It would be nice if some actual examples could be given. The 
help on dub is a bit confusing because the the code is not 
complete.


In addition to the example on the dub page, there are a some in 
the API docs at https://pbackus.github.io/sumtype/sumtype.html 
that go into more detail.


If by "not complete" you mean that they lack a `main` function, 
that's because they're defined as `unittest` blocks in the 
source. This ensures that they are always correct and 
up-to-date with the latest version of sumtype. I hope you will 
agree that having to type `void main()` and a couple braces is 
an acceptable price to pay for such quality assurance. :)


No, I was thinking of the dub page. Is saw the unit tests which 
were better.




Also, I'm curious how one can handle a collection of types 
with match?


Suppose I have SumType!(int, float, string)

and I wanted a generic match on int and float. Something like

(int | float _) => would be awesome, but that is invalid.

[...]


You can do this with a template handler that introspects on the 
type of its argument:


(num) {
alias T = typeof(num);
static assert(is(T == int) || is(T == float));
// code to handle int or float goes here
}

If you want nicer syntax, you can factor out the type 
assertions into a template wrapper:


SumType!(int, float, string) x;
x.match!(
acceptOnly!(int, float,
num => writeln("number")
),
(string _) => writeln("string")
);

Full code here: https://run.dlang.io/is/MrzF5n



Ok, thanks!


Also, a full algebra would be nice ;)

alias X = SumType!(int, float, string)
alias Y = SumType!(complex, vector)

alias Z = SumType.Union(X,Y);

Z is a "super type" as could have been expressed as

alias Z = SumType!(int, float, string, complex, vector).


You can do this already with `alias Z = 
SumType!(NoDuplicates!(X.Types, Y.Types));`, using 
`std.meta.NoDuplicates`. I don't know of an equivalent template 
for getting the intersection of two type sequences, but you 
could probably cobble something together with `std.meta.Filter`.




Yes, but


alias Z = SumType.Union(X,Y);


is not the same as


alias Z = SumType!(int, float, string, complex, vector).


In the first case Z is actually a union of 2 types while in the 
second it is of 5. There is a subtle difference in that in the 
second case the types lose relation. E.g., there is no way to 
recover X or Y from Z but in the first we can:


We can see this explicitly:

union X
{
   int;
   float;
   string;
}


union Y
{
   complex;
   vector;
}

union Z
{
   X;
   Y;
}

union ZZ
{
   int;
   float;
   string;
   complex;
   vector;
}


ZZ is flat while Z is hierarchical.

I'm not sure how SumType deals with type info, if it is local or 
global. If it were global, then Z would definitely be different 
than ZZ.



except, of course, Z would be typed in terms of X and Y.

[...]


What you are describing here is, essentially, an entirely new 
type system. It's interesting as a thought experiment, but 
ultimately, D already has a type system, and I would much 
rather have SumType work with the existing system than invent 
its own.


It's not entirely different but a different representation. 
Ultimately it should be isomorphic.


(Also, what you call `ProdType` already exists. It's called 
`Tuple`, and is located in the module `std.typecons`.)



Yes, Tuple is a product over types, but we are talking about in 
the context of including type info for matching and such which 
tuples don't directly have.


What I'm ultimately talking about is to allow one to compare 
these types, to match, etc in a way that is more sophisticated 
than having to match directly on the types.


E.g., what if we wanted to match on "inheritance"? How can that 
be done?


Using the Z above, We could write a match on X and or Y. This is 
more direct than using ZZ, although we could do somewhat just as 
easy. But suppose we would like to match for anything that uses X?


Z, which uses X, acts very similar to a derived class and this 
info can be used to provide more appropriate matching.



D already has a great type system with it's many advanced 
features but these are pretty much static while the point of sum 
types is to provide dynamic resolution. Maybe some combination 
could be used. Since SumType is already a D type it can use the 
D's typing features but since SumType is effectively sealed in 
this sense it doesn't work too well.


e.g.,

alias Z = SumType!(X,Y) is a type itself and effectively inherits 
from X and Y but this relationship is not expressed in any 
meaningful way in SumType.


Maybe SumType!(X,Y) could return a new type that is a class that 
inherits from X and Y? (unfortunately this can't work because of 
single inheritance but these types could probably be wrapped in 
interfaces and 

Re: sumtype 0.5.0

2018-08-10 Thread Paul Backus via Digitalmars-d-announce

On Friday, 10 August 2018 at 13:11:13 UTC, Everlast wrote:

On Friday, 10 August 2018 at 12:35:18 UTC, Everlast wrote:


It would be nice if some actual examples could be given. The 
help on dub is a bit confusing because the the code is not 
complete.


In addition to the example on the dub page, there are a some in 
the API docs at https://pbackus.github.io/sumtype/sumtype.html 
that go into more detail.


If by "not complete" you mean that they lack a `main` function, 
that's because they're defined as `unittest` blocks in the 
source. This ensures that they are always correct and up-to-date 
with the latest version of sumtype. I hope you will agree that 
having to type `void main()` and a couple braces is an acceptable 
price to pay for such quality assurance. :)


Also, I'm curious how one can handle a collection of types with 
match?


Suppose I have SumType!(int, float, string)

and I wanted a generic match on int and float. Something like

(int | float _) => would be awesome, but that is invalid.

[...]


You can do this with a template handler that introspects on the 
type of its argument:


(num) {
alias T = typeof(num);
static assert(is(T == int) || is(T == float));
// code to handle int or float goes here
}

If you want nicer syntax, you can factor out the type assertions 
into a template wrapper:


SumType!(int, float, string) x;
x.match!(
acceptOnly!(int, float,
num => writeln("number")
),
(string _) => writeln("string")
);

Full code here: https://run.dlang.io/is/MrzF5n


Also, a full algebra would be nice ;)

alias X = SumType!(int, float, string)
alias Y = SumType!(complex, vector)

alias Z = SumType.Union(X,Y);

Z is a "super type" as could have been expressed as

alias Z = SumType!(int, float, string, complex, vector).


You can do this already with `alias Z = 
SumType!(NoDuplicates!(X.Types, Y.Types));`, using 
`std.meta.NoDuplicates`. I don't know of an equivalent template 
for getting the intersection of two type sequences, but you could 
probably cobble something together with `std.meta.Filter`.



except, of course, Z would be typed in terms of X and Y.

[...]


What you are describing here is, essentially, an entirely new 
type system. It's interesting as a thought experiment, but 
ultimately, D already has a type system, and I would much rather 
have SumType work with the existing system than invent its own.


(Also, what you call `ProdType` already exists. It's called 
`Tuple`, and is located in the module `std.typecons`.)


Re: sumtype 0.5.0

2018-08-10 Thread Everlast via Digitalmars-d-announce

On Friday, 10 August 2018 at 12:35:18 UTC, Everlast wrote:

On Thursday, 9 August 2018 at 15:56:12 UTC, Paul Backus wrote:

On Wednesday, 8 August 2018 at 20:54:13 UTC, Paul Backus wrote:
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


Version 0.5.2, with fixes for the bugs reported in this 
thread, is now available. Thanks to vit for their detailed 
feedback!


In order to avoid spamming the newsgroup, I'd like to 
encourage everyone to submit further bug reports, feature 
requests, etc. as Github issues at 
https://github.com/pbackus/sumtype/issues


It would be nice if some actual examples could be given. The 
help on dub is a bit confusing because the the code is not 
complete.


Also, I'm curious how one can handle a collection of types with 
match?


Suppose I have SumType!(int, float, string)

and I wanted a generic match on int and float. Something like

(int | float _) => would be awesome, but that is invalid.

Of course, there are work arounds but the goal is for 
simplification in a canonical way.


One way would be to be able to call other handlers directly:

(int _) => { return match.float(_); }
(float _) => { ... }

which, say, calls the float delegate. This is just "chaining" but 
is a nice universe syntax and is good if it can be implement(with 
inlining occurring).


Another way would be to allow for "sub-SumType'ing":

alias X = SumType!(int, float, string);

(X.SubType!(int, float) _) { ... }

or whatever, again, a few ways one can go about this.

Also, a full algebra would be nice ;)

alias X = SumType!(int, float, string)
alias Y = SumType!(complex, vector)

alias Z = SumType.Union(X,Y);

Z is a "super type" as could have been expressed as

alias Z = SumType!(int, float, string, complex, vector).

except, of course, Z would be typed in terms of X and Y.

The difference is that Z is compatible with X and Y. But this 
might require a little work because something like Z.X is not the 
same as X due to the fact that X's typeinfo(an index value?) 
cannot represent Z(it would be nice if it could but I think it 
might be fragile to do so unless hashing solves the problem).


Then intersection can also be defined:

SumType.Intersect(X,Y) = SumType!(Null) = null;

But if Y had a string then

SumType.Intersect(X,Y) = SumType!(string); (which should reduce 
to string)


But the problem is that, a string in X and a string in Y may have 
no relationship programmatically(one may encode a series of bits 
for, say, compression and the other an error string). This then 
requires some way to know which type is being acted on(X or Y) as 
so the in sub-types can be properly interpreted.



If one notices, this is similar to inheritance in that union is 
related to derivation and intersection to reduction. The notions, 
if they can be consistently defined, allows one to build type 
structures that are hierarchically based and parallel classes. In 
fact, classes could be seen as a product of SumTypes on single 
elements:


alias C = ProdType!(SumType!string, SumType!int);

C then would act like a class with a string and int field with 
the functionality of both combined and


class Q;
alias D = ProdType!(SumType!float, C, Q);

would be a derivation from C and Q and adding a float member.

Of course, this would be a lot of work and require mathematical 
consistency and not offer much over the current method(in which 
ultimately it would be equivalent to I believe) but it does 
provide completeness. Of course, I suppose if we were to go down 
that path we'ed then require a full on algebraic system so we 
could do stuff like exponentiation, etc ;)















Re: sumtype 0.5.0

2018-08-10 Thread Everlast via Digitalmars-d-announce

On Thursday, 9 August 2018 at 15:56:12 UTC, Paul Backus wrote:

On Wednesday, 8 August 2018 at 20:54:13 UTC, Paul Backus wrote:
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


Version 0.5.2, with fixes for the bugs reported in this thread, 
is now available. Thanks to vit for their detailed feedback!


In order to avoid spamming the newsgroup, I'd like to encourage 
everyone to submit further bug reports, feature requests, etc. 
as Github issues at https://github.com/pbackus/sumtype/issues


It would be nice if some actual examples could be given. The help 
on dub is a bit confusing because the the code is not complete.


Re: sumtype 0.5.0

2018-08-09 Thread Paul Backus via Digitalmars-d-announce

On Wednesday, 8 August 2018 at 20:54:13 UTC, Paul Backus wrote:
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


Version 0.5.2, with fixes for the bugs reported in this thread, 
is now available. Thanks to vit for their detailed feedback!


In order to avoid spamming the newsgroup, I'd like to encourage 
everyone to submit further bug reports, feature requests, etc. as 
Github issues at https://github.com/pbackus/sumtype/issues


Re: sumtype 0.5.0

2018-08-09 Thread Paul Backus via Digitalmars-d-announce

On Thursday, 9 August 2018 at 07:52:12 UTC, vit wrote:
SumType without initialization doesn't fail if first type has 
@disabled this().


It did fail for me, but it also failed *with* initialization too, 
so there was definitely a bug there.


method toString is not template. (why is there in the first 
place?)


Mainly it's there so I can do `writeln(sumTypeInst);` without 
writing out the full `match` call every time. I can't see any 
reason why it should be a template.



SumType can have zero TypeArgs but then tryMatch fail:

[...]

struct SumType(TypeArgs...)
if(TypeArgs.length > 0){
}


The second option is what `Algebraic` does, and it seems like the 
most sensible solution to me.


Re: sumtype 0.5.0

2018-08-09 Thread vit via Digitalmars-d-announce

On Thursday, 9 August 2018 at 01:04:43 UTC, Paul Backus wrote:

On Thursday, 9 August 2018 at 00:11:22 UTC, Seb wrote:

On Thursday, 9 August 2018 at 00:07:05 UTC, Seb wrote:
(It uses the version from DUB and updates itself once daily, 
but somehow dub still lists 0.4.1 at the moment)


It looks like you didn't push the git tag to GitHub:

https://github.com/pbackus/sumtype/releases


One of these days, I'll learn. Should be there now.


ignore the message with opAssign, my version doesnt work :)


Re: sumtype 0.5.0

2018-08-09 Thread vit via Digitalmars-d-announce

On Thursday, 9 August 2018 at 01:04:43 UTC, Paul Backus wrote:

...


@safe opAssign can call @system postblit:

bool moved = false;
struct S{
this(this)@system{
moved = true;
}

}
void main()@safe{

auto st = SumType!(S).init;

st = S.init;
assert(moved == true);
}

change:
() @trusted { moveEmplace(rhs, storage.values[i]); }();

to:
moveEmplace(rhs, *(() @trusted => [i])());



Re: sumtype 0.5.0

2018-08-09 Thread vit via Digitalmars-d-announce

On Thursday, 9 August 2018 at 01:04:43 UTC, Paul Backus wrote:

On Thursday, 9 August 2018 at 00:11:22 UTC, Seb wrote:

On Thursday, 9 August 2018 at 00:07:05 UTC, Seb wrote:
(It uses the version from DUB and updates itself once daily, 
but somehow dub still lists 0.4.1 at the moment)


It looks like you didn't push the git tag to GitHub:

https://github.com/pbackus/sumtype/releases


One of these days, I'll learn. Should be there now.


SumType without initialization doesn't fail if first type has 
@disabled this().

In SumType need be something like this:

struct SumType(...){
///...

static if(Types.length > 0 && __traits(compiles, (){Types[0] 
tmp;}()) == false){

///Types[0] has disabled this()
@disable this();
}

///
}

method toString is not template. (why is there in the first 
place?)


SumType can have zero TypeArgs but then tryMatch fail:

auto st = SumType!();
st.tryMatch!((_) => 1);   //fail at compile time with bad error 
message.


in matchImpl can by something like this:

static if(Types.length == 0){
static if(exhaustive) {
static assert(0, "No value to match");
} else {
throw new MatchException("No value to match");
}
}
else{
///...
}

or:

struct SumType(TypeArgs...)
if(TypeArgs.length > 0){
}



Re: sumtype 0.5.0

2018-08-08 Thread Paul Backus via Digitalmars-d-announce

On Thursday, 9 August 2018 at 00:11:22 UTC, Seb wrote:

On Thursday, 9 August 2018 at 00:07:05 UTC, Seb wrote:
(It uses the version from DUB and updates itself once daily, 
but somehow dub still lists 0.4.1 at the moment)


It looks like you didn't push the git tag to GitHub:

https://github.com/pbackus/sumtype/releases


One of these days, I'll learn. Should be there now.


Re: sumtype 0.5.0

2018-08-08 Thread Seb via Digitalmars-d-announce

On Thursday, 9 August 2018 at 00:07:05 UTC, Seb wrote:
(It uses the version from DUB and updates itself once daily, 
but somehow dub still lists 0.4.1 at the moment)


It looks like you didn't push the git tag to GitHub:

https://github.com/pbackus/sumtype/releases


Re: sumtype 0.5.0

2018-08-08 Thread Seb via Digitalmars-d-announce

On Wednesday, 8 August 2018 at 20:54:13 UTC, Paul Backus wrote:
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


Features:
  - Pattern matching, including support for structural matching 
(*)

  - Self-referential types, using `This`
  - Works with `pure`, `@safe`, `@nogc`, and `immutable` (*)
  - Zero runtime overhead compared to hand-written C
- No heap allocation
- Does not rely on runtime type information (`TypeInfo`) (*)

Starred features (*) are those that are missing from 
`Algebraic`.


Code examples are available in the documentation (linked below).

New since the last announced version, 0.3.0:
  - Types with destructors and postblits are now handled 
correctly.
  - Unreachable handlers in `match` calls are now a 
compile-time error.
  - `match` handlers can now operate on the stored value by 
reference.
  - A new method, `tryMatch`, allows for non-exhaustive pattern 
matching.

  - Various small improvements to the documentation.

Documentation: https://pbackus.github.io/sumtype/sumtype.html
DUB: https://code.dlang.org/packages/sumtype
Github: https://github.com/pbackus/sumtype


That's a pretty cool library!
I added it to run.dlang.io, s.t. it's easy for people to play 
with it and share examples, e.g.


https://run.dlang.io/is/5znJXH

(It uses the version from DUB and updates itself once daily, but 
somehow dub still lists 0.4.1 at the moment)


Re: sumtype 0.5.0

2018-08-08 Thread Paul Backus via Digitalmars-d-announce

On Wednesday, 8 August 2018 at 21:57:07 UTC, vit wrote:

Nice,
but destructor SumType.~this() can call destroy on reference 
type like class:


Whoops. Good catch. I've pushed a fix, tagged as version 0.5.1.


Re: sumtype 0.5.0

2018-08-08 Thread vit via Digitalmars-d-announce

On Wednesday, 8 August 2018 at 20:54:13 UTC, Paul Backus wrote:
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


Features:
  - Pattern matching, including support for structural matching 
(*)

  - Self-referential types, using `This`
  - Works with `pure`, `@safe`, `@nogc`, and `immutable` (*)
  - Zero runtime overhead compared to hand-written C
- No heap allocation
- Does not rely on runtime type information (`TypeInfo`) (*)

Starred features (*) are those that are missing from 
`Algebraic`.


Code examples are available in the documentation (linked below).

New since the last announced version, 0.3.0:
  - Types with destructors and postblits are now handled 
correctly.
  - Unreachable handlers in `match` calls are now a 
compile-time error.
  - `match` handlers can now operate on the stored value by 
reference.
  - A new method, `tryMatch`, allows for non-exhaustive pattern 
matching.

  - Various small improvements to the documentation.

Documentation: https://pbackus.github.io/sumtype/sumtype.html
DUB: https://code.dlang.org/packages/sumtype
Github: https://github.com/pbackus/sumtype


Nice,
but destructor SumType.~this() can call destroy on reference type 
like class:


bool destructed = false;
class C{
~this(){
destructed = true;
}
}
struct S{
~this(){
}

}
void main(){
auto c = new C;
{
auto st = SumType!(C, S)(c);
}
assert(destructed == true);
}


sumtype 0.5.0

2018-08-08 Thread Paul Backus via Digitalmars-d-announce
SumType is a generic sum type for modern D. It is meant as an 
alternative to `std.variant.Algebraic`.


Features:
  - Pattern matching, including support for structural matching 
(*)

  - Self-referential types, using `This`
  - Works with `pure`, `@safe`, `@nogc`, and `immutable` (*)
  - Zero runtime overhead compared to hand-written C
- No heap allocation
- Does not rely on runtime type information (`TypeInfo`) (*)

Starred features (*) are those that are missing from `Algebraic`.

Code examples are available in the documentation (linked below).

New since the last announced version, 0.3.0:
  - Types with destructors and postblits are now handled 
correctly.
  - Unreachable handlers in `match` calls are now a compile-time 
error.
  - `match` handlers can now operate on the stored value by 
reference.
  - A new method, `tryMatch`, allows for non-exhaustive pattern 
matching.

  - Various small improvements to the documentation.

Documentation: https://pbackus.github.io/sumtype/sumtype.html
DUB: https://code.dlang.org/packages/sumtype
Github: https://github.com/pbackus/sumtype