Re: Option types and pattern matching.

2015-10-27 Thread Kagamin via Digitalmars-d

On Monday, 26 October 2015 at 16:42:27 UTC, TheFlyingFiddle wrote:
If you instead use pattern matching as in your example you have 
much better context information that can actually help you do 
something in the case a value is not there.


Probably possible:

Some!T get(T)(Option!T item) {
Some!T r;
//Static guarantee of handling value not present
item match {
None() => {
throw new Exception("empty!");
}
Some(t) => {
r=t;
}
}

return r;
}

Then:
Option!File file;
Some!File s = file.get();


Re: Option types and pattern matching.

2015-10-27 Thread TheFlyingFiddle via Digitalmars-d

On Tuesday, 27 October 2015 at 07:55:46 UTC, Kagamin wrote:
On Monday, 26 October 2015 at 16:42:27 UTC, TheFlyingFiddle 
wrote:
If you instead use pattern matching as in your example you 
have much better context information that can actually help 
you do something in the case a value is not there.


Probably possible:

Some!T get(T)(Option!T item) {
Some!T r;
//Static guarantee of handling value not present
item match {
None() => {
throw new Exception("empty!");
}
Some(t) => {
r=t;
}
}

return r;
}

Then:
Option!File file;
Some!File s = file.get();


Sure that would work but i don't see that it's different then an 
enfore since you don't have access the context where get is 
invoked so you can't really do anything with it.


Contrived Example:

void foo()
{
   Option!File worldFile = getAFile("world.json");
   auto world  = parseJSON(worldFile.get());
   Option!File mapFile = getAFile(world["map"]);
   auto map= parseJSON(mapFile.get());
   //Other stuff.
}

Let's say we get an NoObjectException, this tells us that either 
the world.json file did not exist or the map file does not exist. 
get does not have access to that context so we wouldn't be able 
to tell. This next example would fix this.


void foo()
{
   Option!File worldFile = getAFile("world.json");
   enforce(worldFile.hasValue, "Error while loading file: 
world.json");

   auto world = parseJSON(worldFile.get());
   Option!File mapFile = getAFile(world["map"]);
   enforce(mapFile.hasValue, "Error while loading file: " ~ 
world["map"]);

   auto map = parseJSON(mapFile.get());
   //Other stuff
}

Now we know which file failed to load. But we bypassed the 
NoObjectException to do it.


I would prefer this style instead.
void foo()
{
  Option!File worldFile = getAFile("world.json");
  match worldFile {
 Some(value) => {
 auto world  = parseJSON(value);
 Option!File mapFile = getAFile(world["map"]);
 match mapFile {
Some(mapf) => {
   auto map = parseJSON(mapf);
   //Do something here.
},
None => enforce(false, "Failed to load: " ~ 
world["map"]);

 }
 },
 None => enforce(false, "Failed to load: world.json");
   }
}

The reason that I prefer that is not that I like the syntax 
really. It's just that if the only way to get a value is to 
pattern match on it then you are forced to consider the case 
where the value was not there.



Guess a D version without language support would look something 
like:

void foo()
{
  auto worldFile = getAFile("world.json");
  worldFile.match!(
 (File worldf) {
auto world = parseJSON(value);
auto mapFile = getAFile(world["map"]);
mapFile.match!(
   (File mapf)
   {
  auto map = parseJSON(mapf);
  //Do stuff;
   },
   (None) => enforce(false, "Failed to load: " ~ 
world["map"]);

 },
 (None) => enforce(false, "Failed to load: world.json")
  );
}

The example here is very contrived. Here we just throw exceptions 
if the file could not load and if that is all we do we should 
just wrap getAFile instead but i hope you get my point.







Re: Option types and pattern matching.

2015-10-27 Thread Kagamin via Digitalmars-d
On Tuesday, 27 October 2015 at 15:06:07 UTC, TheFlyingFiddle 
wrote:
The reason that I prefer that is not that I like the syntax 
really. It's just that if the only way to get a value is to 
pattern match on it then you are forced to consider the case 
where the value was not there.


If pattern matching is the only way, the get function above will 
still work: it uses only pattern matching, nothing else.


Re: Option types and pattern matching.

2015-10-27 Thread Meta via Digitalmars-d
On Tuesday, 27 October 2015 at 15:06:07 UTC, TheFlyingFiddle 
wrote:

I would prefer this style instead.
void foo()
{
  Option!File worldFile = getAFile("world.json");
  match worldFile {
 Some(value) => {
 auto world  = parseJSON(value);
 Option!File mapFile = getAFile(world["map"]);
 match mapFile {
Some(mapf) => {
   auto map = parseJSON(mapf);
   //Do something here.
},
None => enforce(false, "Failed to load: " ~ 
world["map"]);

 }
 },
 None => enforce(false, "Failed to load: world.json");
   }
}


This can arguably already be done cleaner in D.

if (auto worldFile = getAFile("world.json"))
{
auto world = parseJSON(worldFile);
if (auto mapFile = getAFile(world["map"))
{
//...
}
else enforce(false, "Failed to load: " ~ world["map"]);
}
else enforce(false, "Failed to load: world.json");

Or even:

auto worldFile = enforce(getAFile("world.json"), "Failed to load: 
world.json");

auto world = parseJSON(worldFile);
auto mapFile = enforce(getAFile(world["map"]), "Failed to load: " 
~ world["map"]);

//...

From what I've seen in the Rust community, they try to avoid 
using match as it's very syntactically heavy. They have all kinds 
of idioms to avoid doing matches on Option, such as the try! 
macro, unwrap_or, unwrap_or_else, etc.


That being said, pattern matching has been one of my most-wanted 
D features for years.


Re: Option types and pattern matching.

2015-10-27 Thread TheFlyingFiddle via Digitalmars-d

On Tuesday, 27 October 2015 at 17:48:04 UTC, Meta wrote:
On Tuesday, 27 October 2015 at 15:06:07 UTC, TheFlyingFiddle 
wrote:

This can arguably already be done cleaner in D.

if (auto worldFile = getAFile("world.json"))
{
auto world = parseJSON(worldFile);
if (auto mapFile = getAFile(world["map"))
{
//...
}
else enforce(false, "Failed to load: " ~ world["map"]);
}
else enforce(false, "Failed to load: world.json");

Or even:

auto worldFile = enforce(getAFile("world.json"), "Failed to 
load: world.json");

auto world = parseJSON(worldFile);
auto mapFile = enforce(getAFile(world["map"]), "Failed to load: 
" ~ world["map"]);

//...

From what I've seen in the Rust community, they try to avoid 
using match as it's very syntactically heavy. They have all 
kinds of idioms to avoid doing matches on Option, such as the 
try! macro, unwrap_or, unwrap_or_else, etc.


That being said, pattern matching has been one of my 
most-wanted D features for years.


Yes this is much cleaner. But it does not really force a user to 
consider the empty case.


I mean this would still compile.
auto worldFile = getAFile("world.json");
auto world = parseJSON(worldFile);
auto mapFile   = getAFile(world["map"]);
auto map   = parseJSON(mapFile);

What I was after was a way to at compile time ensure that all 
accesses to the value in the optional type are considered. From 
my uses of Maybe in haskell i know this get's annoying quick so I 
dunno if it's a good thing. But atleast you would know that in 
all places that optionals are accessed a handler for the empty 
case would be present.


Re: Option types and pattern matching.

2015-10-26 Thread Kagamin via Digitalmars-d

For Option see https://w0rp.com/project/dstruct/dstruct/option/


Re: Option types and pattern matching.

2015-10-26 Thread Jacob Carlborg via Digitalmars-d

On 2015-10-25 19:23, Dmitry Olshansky wrote:


I humbly believe that D may just add special re-write rule to the switch
statement in order to allow user-defined switchable types. This goes
along nicely with the trend - e.g. foreach statement works with anything
having static range interfaces or opApply.


Do you think that could handle all different type of patterns? For 
example extractor patterns:


match x {
case Foo(bar) => println(bar)
}

The above is Scala.

--
/Jacob Carlborg


Re: Option types and pattern matching.

2015-10-26 Thread Edmund Smith via Digitalmars-d

On Sunday, 25 October 2015 at 06:22:51 UTC, TheFlyingFiddle wrote:

On Sunday, 25 October 2015 at 05:45:15 UTC, Nerve wrote:
On Sunday, 25 October 2015 at 05:05:47 UTC, Rikki Cattermole 
wrote:
Since I have no idea what the difference between Some(_), 
None and default. I'll assume it's already doable.


_ represents all existing values not matched. In this case, 
Some(_) represents any integer value that is not 7. None 
specifically matches the case where no value has been 
returned. We are, in most languages, also able to unwrap the 
value:


match x {
Some(7) => "Lucky number 7!",
Some(n) => "Not a lucky number: " ~ n,
None => "No value found"
}


You can do something very similar to that. With slightly 
different syntax.


import std.traits;
import std.conv;
import std.variant;
struct CMatch(T...) if(T.length == 1)
{
   alias U = typeof(T[0]);
   static bool match(Variant v)
   {
  if(auto p = v.peek!U)
 return *p == T[0];
  return false;
   }
}

auto ref match(Handlers...)(Variant v)
{
   foreach(handler; Handlers)
   {
  alias P = Parameters!handler;
  static if(P.length == 1)
  {
 static if(isInstanceOf!(CMatch, P[0]))
 {
if(P[0].match(v))
   return handler(P[0].init);
 }
 else
 {
if(auto p = v.peek!(P[0]))
   return handler(*p);
 }
  }
  else
  {
 return handler();
  }
   }

   assert(false, "No matching pattern");
}

unittest
{
Variant v = 5;
string s = v.match!(
(CMatch!7) => "Lucky number seven",
(int n)=> "Not a lucky number: " ~ n.to!string,
() => "No value found!");

   writeln(s);
}


You could also emulate constant matching using default parameters 
(albeit with the restriction that they must be after any 
non-default/constant parameters), since the defaults form part of 
the function's type. I tried making something like this earlier 
this summer and it'd check that a given value was first equal to 
the default parameter and match if so, or match if there was no 
default parameter but the types matched.


e.g.
//template ma(tch/g)ic

unittest
{
Algebraic!(string, int, double, MyStruct) v = 5;
string s = v.match!(
(string s = "") => "Empty string!",
(string s) => s,
(int i = 7) => "Lucky number 7",
(int i = 0) => "Nil",
(int i) => i.to!string,
(double d) => d.to!string,
(MyStruct m = MyStruct(15)) => "Special MyStruct value",
(MyStruct m) => m.name, //
() => "ooer");
writeln(s);
}

It's a bit ugly overloading language features like this, but it 
makes the syntax a little prettier.


I'd really like to see proper pattern matching as a 
language-level feature however; for all the emulating it we can 
do in D, it's not very pretty or friendly and optimising it is 
harder since the language has no concept of pattern matching. 
Things like Option (and other ADTs) are lovely, but really need 
good pattern matching to become worthwhile IMO (e.g. Java 
Optional has a get() method that throws on empty, which 
undermines the main reason to use optional - to have a guarantee 
that you handle the empty case gracefully; Scala's Option is 
really nice on the other hand since you can/should pattern match).


Re: Option types and pattern matching.

2015-10-26 Thread Dmitry Olshansky via Digitalmars-d

On 26-Oct-2015 12:16, Jacob Carlborg wrote:

On 2015-10-25 19:23, Dmitry Olshansky wrote:


I humbly believe that D may just add special re-write rule to the switch
statement in order to allow user-defined switchable types. This goes
along nicely with the trend - e.g. foreach statement works with anything
having static range interfaces or opApply.


Do you think that could handle all different type of patterns? For
example extractor patterns:

match x {
 case Foo(bar) => println(bar)
}

The above is Scala.



Scala does it with a bit of re-writes and partially hard-wired logic 
(mostly to optimize). I don't see a problem with it and D would be 
better off with something similar.


Current switch is both too lax (accepts variables) and too rigid (only 
integers, string and soon(?) pointers).


--
Dmitry Olshansky


Re: Option types and pattern matching.

2015-10-26 Thread TheFlyingFiddle via Digitalmars-d

On Monday, 26 October 2015 at 11:40:09 UTC, Edmund Smith wrote:
On Sunday, 25 October 2015 at 06:22:51 UTC, TheFlyingFiddle 
wrote:
You could also emulate constant matching using default 
parameters (albeit with the restriction that they must be after 
any non-default/constant parameters), since the defaults form 
part of the function's type. I tried making something like this 
earlier this summer and it'd check that a given value was first 
equal to the default parameter and match if so, or match if 
there was no default parameter but the types matched.


e.g.
//template ma(tch/g)ic

unittest
{
Algebraic!(string, int, double, MyStruct) v = 5;
string s = v.match!(
(string s = "") => "Empty string!",
(string s) => s,
(int i = 7) => "Lucky number 7",
(int i = 0) => "Nil",
(int i) => i.to!string,
(double d) => d.to!string,
(MyStruct m = MyStruct(15)) => "Special MyStruct value",
(MyStruct m) => m.name, //
() => "ooer");
writeln(s);
}

It's a bit ugly overloading language features like this, but it 
makes the syntax a little prettier.

This does look nicer indeed.


Why not just use a value as an extra argument:
v.match!(
   7, (int i) => "Lucky number 7"
);
I like this you could go further with this to allow any number of 
constants.

v.match!(
5, 7,   i => "Was: " ~ i.to!string,
(int i)   => "Was this: " ~ i.to!string);

Or for ranges.
v.match!(
MatchR!(1, 10), i => "Was: " ~ i.to!string, //Matches 1 .. 10
(int i)  => "Was this: " ~ i.to!string);

I'd really like to see proper pattern matching as a 
language-level feature however; for all the emulating it we can 
do in D, it's not very pretty or friendly and optimising it is 
harder since the language has no concept of pattern matching.

One could probably get something like this:

int i = 5;
string s = i.match!(
   5, 7, n => "Five or seven",
   MatchR!(10, 100), n => "Was between ten and a hundred",
   (n) => "Was: " ~ n.to!string);

to fold into something like this:

void match(T...)(int i)
{
   switch(i)
   {
  case 5: case 7: return (T[2])!int(i);
  case 10: .. case 99: return (T[3])!int(i);
  default: return (T[4])!int(i);
   }
}

int i = 5;
string s = match!(/* lambdas and whatnot */), i);

With some template/ctfe and string mixings magic.

In-lining, constant folding etc could probably just reduce it to
the equvalent of:

int i= 5;
string s = "Five or seven";

(if there is really good constant folding :P)

It might however generate lot's of useless symbols in the 
resulting code

making code size's larger.

Things like Option (and other ADTs) are lovely, but really need 
good pattern matching to become worthwhile IMO (e.g. Java 
Optional has a get() method that throws on empty, which 
undermines the main reason to use optional -
Another thing that has always bothered me with Optional in 
Java in addition to this is that the optional value itself might 
be null. So to write robust code you first have to check against 
null on the option value :P.


Scala's Option is really nice on the other hand since you 
can/should pattern match).
Don't really see a point in an optional type if can access the 
underlying

value without first checking if it's there.






Re: Option types and pattern matching.

2015-10-26 Thread Jacob Carlborg via Digitalmars-d

On 2015-10-26 12:40, Edmund Smith wrote:


You could also emulate constant matching using default parameters
(albeit with the restriction that they must be after any
non-default/constant parameters), since the defaults form part of the
function's type. I tried making something like this earlier this summer
and it'd check that a given value was first equal to the default
parameter and match if so, or match if there was no default parameter
but the types matched.

e.g.
//template ma(tch/g)ic

unittest
{
 Algebraic!(string, int, double, MyStruct) v = 5;
 string s = v.match!(
 (string s = "") => "Empty string!",
 (string s) => s,
 (int i = 7) => "Lucky number 7",
 (int i = 0) => "Nil",
 (int i) => i.to!string,
 (double d) => d.to!string,
 (MyStruct m = MyStruct(15)) => "Special MyStruct value",
 (MyStruct m) => m.name, //
 () => "ooer");
 writeln(s);
}

It's a bit ugly overloading language features like this, but it makes
the syntax a little prettier.


Why not just use a value as an extra argument:

v.match!(
7, (int i) => "Lucky number 7"
);


Scala's Option is really nice on the other hand since you can/should pattern 
match).


I thought it was preferred to use the higher order functions like "map" 
and "filter".


--
/Jacob Carlborg


Re: Option types and pattern matching.

2015-10-26 Thread Edmund Smith via Digitalmars-d

On Monday, 26 October 2015 at 14:13:20 UTC, TheFlyingFiddle wrote:

On Monday, 26 October 2015 at 11:40:09 UTC, Edmund Smith wrote:
Scala's Option is really nice on the other hand since you 
can/should pattern match).
Don't really see a point in an optional type if can access the 
underlying

value without first checking if it's there.


The key difference with (exhaustive) pattern matching is that it 
*is* the check that the value is there. Pattern matching enforces 
the existence of an on-nothing clause for Optional, on-error for 
Error, on-Leaf and on-Branch for Bintrees, etc.
And even with nice higher-order functions, plain pattern matching 
is quite valuable for finely controlled error/resource handling, 
and I often see it in Rust code as well as Scala (and I've seen 
it used in Haskell occasionally too). A brief, contrived example 
use-case:


//External code that disallows monadic int[]
void processThatMustOccur(int[] data);
...
Option!File findFile(string fname);
Result!(int[]) parseInts(File file);

//verbose for clarity
void parseMatches(string path) {
Option!File ofile = path.findFile();

//Static guarantee of handling value not present
ofile match {
None() => {
//Handle error for no file found, retry with new path
}
//The existence of file is effectively proof that ofile 
is present

Some(file) => {
Option!(int[]) odata = file.parseInts();
odata match {
Success(data) => 
processThatMustOccur(preProcess(data));

Error(error) =>
//Handle error for bad parse, backtrack 
depends on error

}
}
}

//Continue after processing data
}

void parseMonadic(string path) {
path.findFile()
.bind!parseInts()
.bind!(map!preProcess)
.bind!processThatMustOccur()
.onFailure!backTrack
//How can we backtrack a variable amount easily?

//Continue after processing data
}

The error control loss can be mostly avoided by using an 
appropriate error monad or API design, but there's still the 
issue of interfacing with non-monadic code.
It essentially provides a guarantee that the equivalent of 'T 
get();' will handle errors, like having a checked exception 
version 'T get() throws OnNotPresent;' instead. It also scales up 
much better than having these checked exceptions on not-present 
ADT accesses.


Re: Option types and pattern matching.

2015-10-26 Thread TheFlyingFiddle via Digitalmars-d

On Monday, 26 October 2015 at 15:58:38 UTC, Edmund Smith wrote:
On Monday, 26 October 2015 at 14:13:20 UTC, TheFlyingFiddle 
wrote:

On Monday, 26 October 2015 at 11:40:09 UTC, Edmund Smith wrote:
Scala's Option is really nice on the other hand since you 
can/should pattern match).
Don't really see a point in an optional type if can access the 
underlying

value without first checking if it's there.


The key difference with (exhaustive) pattern matching is that 
it *is* the check that the value is there. Pattern matching 
enforces the existence of an on-nothing clause for Optional, 
on-error for Error, on-Leaf and on-Branch for Bintrees, etc.
And even with nice higher-order functions, plain pattern 
matching is quite valuable for finely controlled error/resource 
handling, and I often see it in Rust code as well as Scala (and 
I've seen it used in Haskell occasionally too). A brief, 
contrived example use-case:


What I meant is that I don't really see the point in optionals 
that look something like this:


struct Optional(T)
{
   T value;
   bool empty;

   ref T get()
   {
  enforce(!empty, "Value not present!");
  return value;
   }

   //Stuff...
}

Optional!(int[]) doSomething(...);
void process(int[]);

void foo()
{
Optional!(int[]) result = doSomething(...);
process(result.get());
}

I mean sure you get a null check in foo instead of in process but 
this style of writing code does not really give you much of an 
advantage since you can't really handle the errors much better 
then you could a Null exception.


If you instead use pattern matching as in your example you have 
much better context information that can actually help you do 
something in the case a value is not there.


Re: Option types and pattern matching.

2015-10-25 Thread Rikki Cattermole via Digitalmars-d

On 25/10/15 7:05 PM, Nerve wrote:

On Sunday, 25 October 2015 at 05:53:32 UTC, Rikki Cattermole wrote:

I'm pretty sure e.g. opEquals/opCmp should work here.
Shouldn't need to switch upon a primitive type. Theoretically could do
it on a e.g. struct. Which has the special comparison that you want.


Hm...these are boolean operators. This means we can only compare two
cases at a time, does it not? Negates the strength of a switch/pattern
match, unless there's something I'm missing.


Well you only need to compare two cases.
I need to spend a bit of time, to see if what I think can be done, is 
actually possible. Essentially toying with your 'Some' types comparison 
rules.



What are these variable length structs you mention, and their special
comparisons? How would we use them?


Oh the idea was a complete flop. It's just an example of how welcoming 
ideas are. Just no guarantee they'll make it to even a consideration 
from Walter.




Re: Option types and pattern matching.

2015-10-25 Thread Nerve via Digitalmars-d
On Sunday, 25 October 2015 at 05:53:32 UTC, Rikki Cattermole 
wrote:

I'm pretty sure e.g. opEquals/opCmp should work here.
Shouldn't need to switch upon a primitive type. Theoretically 
could do it on a e.g. struct. Which has the special comparison 
that you want.


Hm...these are boolean operators. This means we can only compare 
two cases at a time, does it not? Negates the strength of a 
switch/pattern match, unless there's something I'm missing.


What are these variable length structs you mention, and their 
special comparisons? How would we use them?





Re: Option types and pattern matching.

2015-10-25 Thread cym13 via Digitalmars-d

On Sunday, 25 October 2015 at 05:45:15 UTC, Nerve wrote:
On Sunday, 25 October 2015 at 05:05:47 UTC, Rikki Cattermole 
wrote:
Since I have no idea what the difference between Some(_), None 
and default. I'll assume it's already doable.


_ represents all existing values not matched. In this case, 
Some(_) represents any integer value that is not 7. None 
specifically matches the case where no value has been returned. 
We are, in most languages, also able to unwrap the value:


match x {
Some(7) => "Lucky number 7!",
Some(n) => "Not a lucky number: " ~ n,
None => "No value found"
}

Or something to that effect. The equivalent switch statement 
right now would be:


if (x.hasValue()) {
switch (*x.peek!(int)) {
case 7:writeln("Lucky number seven!"); break;
default:   writeln("Not a lucky number: ", 
*x.peek!(int)); break;

}
} else {
writeln("No value.");
}

This does not return a value (is a procedural structure); the 
switch cannot match null; in order to unwrap, we must call 
peek() again; and between the extra if-else and the break 
statements, this is not as clean.


As a note, pattern matching could almost be considered an 
extended form of the ?: operator, which matches over value 
cases rather than boolean truthiness.


Apologies if this is all below you, I'm not in Andrei's or 
Walter's league, just an interested party trying to make 
suggestions to better the language.


Although it doesn't exactly fit the problem at hand I'd like to 
mention
predSwitch. For most cases it does what you could expect from 
pattern
matching I think. Here is an example showing its strength and 
limits on your

showcase:

import std.conv;
import std.stdio;
import std.algorithm.comparison;
import std.variant;


void main(string[] args) {
Variant x;

x = 42;

if (x.hasValue) {
x.predSwitch!((a,b) => *a.peek!int == b) (
7,  "Lucky number!",
42, "This should be a lucky number too!",
"No luck, the number was " ~ x.to!string
).writeln;
}
else {
writeln("No value");
}
}


Re: Option types and pattern matching.

2015-10-25 Thread Rikki Cattermole via Digitalmars-d

On 25/10/15 7:15 PM, Rikki Cattermole wrote:

On 25/10/15 7:05 PM, Nerve wrote:

On Sunday, 25 October 2015 at 05:53:32 UTC, Rikki Cattermole wrote:

I'm pretty sure e.g. opEquals/opCmp should work here.
Shouldn't need to switch upon a primitive type. Theoretically could do
it on a e.g. struct. Which has the special comparison that you want.


Hm...these are boolean operators. This means we can only compare two
cases at a time, does it not? Negates the strength of a switch/pattern
match, unless there's something I'm missing.


Well you only need to compare two cases.
I need to spend a bit of time, to see if what I think can be done, is
actually possible. Essentially toying with your 'Some' types comparison
rules.


What are these variable length structs you mention, and their special
comparisons? How would we use them?


Oh the idea was a complete flop. It's just an example of how welcoming
ideas are. Just no guarantee they'll make it to even a consideration
from Walter.


Just alter the value in v1 under the main function, to see the different 
behaviors.


It's slightly backwards, you expect what is default to be a case. I'm 
sure you can handle changing the logic of opEquals to be what you want.


Also to get a version of Foo that has haveVal as false, probably should 
be a static method, instead of that custom usage. Not to mention 
templating it ext. ext.


struct Foo {
int val;
bool haveVal = true;

alias val this;

bool opEquals(Foo f) {
if (haveVal == f.haveVal) {
if (haveVal)
return val == f.val;
else
return true;
} else
return false;
}
}

void main() {
import std.stdio : writeln;
Foo v1 = Foo(0, false);

switch(v1) {
case 8:
writeln(9);
break;
case 6:
writeln(6);
break;
case Foo(0, false):
writeln("no value");
break;
default:
writeln("unknown: ", v1.val);
break;
}
}



Re: Option types and pattern matching.

2015-10-25 Thread Jacob Carlborg via Digitalmars-d

On 2015-10-25 06:01, Nerve wrote:

Hello D community! First time poster, I'm utterly fascinated with this
language's mix of features. It's powerful and expressive.

There are just two conveniences I'd like to see out of D. The first is
pattern matching, a functional construct which can unwrap tuples or
other containers, usually evaluates to a value (in most languages), and
which almost always idiomatically enforces the programmer to match over
every possible case of a given type.

While ML-inspired languages like F# and OCaml have some good pattern
matching syntax, it's not syntax which would fit in to D; I suggest
taking inspiration of Rusts's matching construct.

match x {
 Some(7) => "Lucky number 7!",
 Some(_) => "No lucky number.",
 None => "No value found"
}

 From that bit of code, we can see another feature at work: The Option
type. It wraps another type (i.e. Option int, Option Car) and represents
a wrapped presence of a value of that type (Some(n), Some(aCar)) or an
absence of that type (None).

Combined with pattern matching, we end up with a safe, functional
construct which can replace a switch statement in most cases, returns a
value, is incredibly compact and readable, and can be used with Options
to ensure that we always account for the possibility of a value not
present, eliminating a whole class of errors when we use it judiciously.

My only concern is that switch statements, while horrendous
syntactically, are extremely performant and essentially compile to a
series of branches.

Are there any counter-arguments for the implementation of these two
features? Is D in a state where language additions have come to a stop?


Both of these can be implemented in library code. Although the syntax 
won't be as nice.


I wouldn't mind having pattern matching as a language feature.

--
/Jacob Carlborg


Re: Option types and pattern matching.

2015-10-25 Thread TheFlyingFiddle via Digitalmars-d

On Sunday, 25 October 2015 at 05:45:15 UTC, Nerve wrote:
On Sunday, 25 October 2015 at 05:05:47 UTC, Rikki Cattermole 
wrote:
Since I have no idea what the difference between Some(_), None 
and default. I'll assume it's already doable.


_ represents all existing values not matched. In this case, 
Some(_) represents any integer value that is not 7. None 
specifically matches the case where no value has been returned. 
We are, in most languages, also able to unwrap the value:


match x {
Some(7) => "Lucky number 7!",
Some(n) => "Not a lucky number: " ~ n,
None => "No value found"
}


You can do something very similar to that. With slightly 
different syntax.


import std.traits;
import std.conv;
import std.variant;
struct CMatch(T...) if(T.length == 1)
{
   alias U = typeof(T[0]);
   static bool match(Variant v)
   {
  if(auto p = v.peek!U)
 return *p == T[0];
  return false;
   }
}

auto ref match(Handlers...)(Variant v)
{
   foreach(handler; Handlers)
   {
  alias P = Parameters!handler;
  static if(P.length == 1)
  {
 static if(isInstanceOf!(CMatch, P[0]))
 {
if(P[0].match(v))
   return handler(P[0].init);
 }
 else
 {
if(auto p = v.peek!(P[0]))
   return handler(*p);
 }
  }
  else
  {
 return handler();
  }
   }

   assert(false, "No matching pattern");
}

unittest
{
Variant v = 5;
string s = v.match!(
(CMatch!7) => "Lucky number seven",
(int n)=> "Not a lucky number: " ~ n.to!string,
() => "No value found!");

   writeln(s);
}



Re: Option types and pattern matching.

2015-10-25 Thread Nerve via Digitalmars-d

On Sunday, 25 October 2015 at 06:22:51 UTC, TheFlyingFiddle wrote:
You can do something very similar to that. With slightly 
different syntax.


import std.traits;
import std.conv;
import std.variant;
struct CMatch(T...) if(T.length == 1)
{
   alias U = typeof(T[0]);
   static bool match(Variant v)
   {
  if(auto p = v.peek!U)
 return *p == T[0];
  return false;
   }
}

auto ref match(Handlers...)(Variant v)
{
   foreach(handler; Handlers)
   {
  alias P = Parameters!handler;
  static if(P.length == 1)
  {
 static if(isInstanceOf!(CMatch, P[0]))
 {
if(P[0].match(v))
   return handler(P[0].init);
 }
 else
 {
if(auto p = v.peek!(P[0]))
   return handler(*p);
 }
  }
  else
  {
 return handler();
  }
   }

   assert(false, "No matching pattern");
}

unittest
{
Variant v = 5;
string s = v.match!(
(CMatch!7) => "Lucky number seven",
(int n)=> "Not a lucky number: " ~ n.to!string,
() => "No value found!");

   writeln(s);
}


That is actually freaking incredible. It evaluates to a value, 
unwraps values, matches against the None case...I guess the only 
thing it doesn't do is have compiler-enforced matching on all 
cases. Unless I'm just slow this morning and not thinking of 
other features a pattern match should have.


Re: Option types and pattern matching.

2015-10-25 Thread TheFlyingFiddle via Digitalmars-d

On Sunday, 25 October 2015 at 14:43:25 UTC, Nerve wrote:
On Sunday, 25 October 2015 at 06:22:51 UTC, TheFlyingFiddle 
wrote:
That is actually freaking incredible. It evaluates to a value, 
unwraps values, matches against the None case...I guess the 
only thing it doesn't do is have compiler-enforced matching on 
all cases. Unless I'm just slow this morning and not thinking 
of other features a pattern match should have.


With some changes to the match function one could enforce that a 
default handler is always present so that all cases are handled 
or error on compilation if it's not.


Something like: (naive way)

auto ref match(Handlers...)(Variant v)
{
//Default handler must be present and be the last handler.
static assert(Parameters!(Handlers[$ - 1]).length == 0,
  "Matches must have a default handler.");
}

now

//Would be a compiler error.
v.match!((int n) => n.to!string));

//Would work.
v.match!((int n) => n.to!string),
 () => "empty");

Additionally one could check that all return types share a common 
implicit conversion type. And cast to that type in the match.


//Returns would be converted to long before being returned.
v.match!((int n)  => n, //Returns int
 (long n) => n, //Returns long
 ()   => 0);

Or if they don't share a common implicit conversion type return a 
Variant result.


Also the handlers could be sorted so that the more general 
handlers are tested later.


//Currently
v.match!((int n) => n,
 (CMatch!7) => 0,
 () => 0);

Would not really work since (int n) is tested for first so 
CMatch!7 would never get called even if the value was 7. But if 
we sort the incoming Handlers with CMatch instances at the front 
then the above would work as a user intended. This would also 
allow the empty/default case to be in any order.


For even more error checking one could make sure that no CMatch 
value intersects with another. That way if there are for example 
two cases with CMatch!7 then an assert error would be emited.


So:
v.match!((CMatch!7) => "The value 7",
 (CMatch!7) => "A seven value",
 () => "empty");

Would error with something like "duplicate value in match"

Other extensions one could do to the pattern matching is:

1. Allow more then one value in CMatch. So CMatch!(5, 7) would 
mean either 5 or 7.
2. Rust has a range syntax, this could be kind of nice. Maybe 
RMatch!(1, 10) for that.

3. Add a predicate match that takes a lambda.

//Predicate match.
struct PMatch(alias lambda)
{
alias T = Parameters!(lambda)[0];
alias this value;
T value;
static bool match(Variant v)
{
   alias P = Parameters!lambda;
   if(auto p = v.peek!P)
   {
  if(lambda(*p))
  {
  value = *p;
  return true;
  }
   }
   return false;
}
}

struct RMatch(T...) if(T.length == 2)
{
   alias C = CommonType!(typeof(T[0]), typeof(T[1]));
   C value;
   alias this value;

   static bool match(Variant v)
   {
  if(auto p = v.peek!C)
  {
 if(*p >= T[0] && *p < T[1])
 {
 value = *p;
 return true;
 }
  }
  return false;
   }
}

v.match!(
  (RMatch!(1, 10) n) => "Was (1 .. 10): " ~ n.to!string;
  (PMatch!((int x) => x % 2 == 0) n) => "Was even: " ~ 
n.to!string,
  (PMatch!((int x) => x % 2 == 1) n) => "Was odd:  " ~ 
n.to!string,

  () => "not an integer");

The PMatch syntax is not the most fun... It can be reduced 
slightly if your not using a variant but a Maybe!T type or a 
regular old type to.


The pattern matching can have more static checks and the syntax 
can look a somewhat better if we are matching on a Maybe!T type 
or a regular type instead of a variant. We could for example make 
sure that all CMatch/RMatch values have the correct type and (in 
some limited cases) ensure that all cases are covered without the 
need for a default switch.


All in all I think that something like this would be a fairly 
comprehensive library pattern matching solution. Catching many 
types of programming errors at compile-time. It could be fast as 
well if all the constants and ranges are converted into a switch 
statements (via string mixin magic).


This problem has gained my interest and I plan on implementing 
this sometime this week. I'll post a link to the source when it's 
done if anyone is interested in it.













Re: Option types and pattern matching.

2015-10-25 Thread Dmitry Olshansky via Digitalmars-d

On 25-Oct-2015 08:01, Nerve wrote:

Hello D community! First time poster, I'm utterly fascinated with this
language's mix of features. It's powerful and expressive.

There are just two conveniences I'd like to see out of D. The first is
pattern matching, a functional construct which can unwrap tuples or
other containers, usually evaluates to a value (in most languages), and
which almost always idiomatically enforces the programmer to match over
every possible case of a given type.

While ML-inspired languages like F# and OCaml have some good pattern
matching syntax, it's not syntax which would fit in to D; I suggest
taking inspiration of Rusts's matching construct.

match x {
 Some(7) => "Lucky number 7!",
 Some(_) => "No lucky number.",
 None => "No value found"
}



I humbly believe that D may just add special re-write rule to the switch 
statement in order to allow user-defined switchable types. This goes 
along nicely with the trend - e.g. foreach statement works with anything 
having static range interfaces or opApply.


All in all we've seen a lot of examples of how it's done in the library 
but always somewhat cumbersome. The next big problem would be that 
switch is a statement not expression which limits use-cases of 
user-defined pattern matching.



--
Dmitry Olshansky


Re: Option types and pattern matching.

2015-10-25 Thread TheFlyingFiddle via Digitalmars-d
On Sunday, 25 October 2015 at 18:23:42 UTC, Dmitry Olshansky 
wrote:
I humbly believe that D may just add special re-write rule to 
the switch statement in order to allow user-defined switchable 
types. This goes along nicely with the trend - e.g. foreach 
statement works with anything having static range interfaces or 
opApply.


I don't think I understand this, could you elaborate?


Re: Option types and pattern matching.

2015-10-25 Thread John Colvin via Digitalmars-d

On Sunday, 25 October 2015 at 18:15:20 UTC, TheFlyingFiddle wrote:
This problem has gained my interest and I plan on implementing 
this sometime this week. I'll post a link to the source when 
it's done if anyone is interested in it.


Without having looked at this in detail, phobos should have a 
good generic implementation of pattern matching, so you should 
consider creating a pull request (std.functional would be the 
natural home I think).


Re: Option types and pattern matching.

2015-10-25 Thread Jacob Carlborg via Digitalmars-d

On 2015-10-25 20:00, John Colvin wrote:


Without having looked at this in detail, phobos should have a good
generic implementation of pattern matching, so you should consider
creating a pull request (std.functional would be the natural home I think).


I've been waiting for this PR [1] to get merged before implementing 
pattern matching as a library function.


[1] https://github.com/D-Programming-Language/dmd/pull/5201

--
/Jacob Carlborg


Re: Option types and pattern matching.

2015-10-24 Thread Rikki Cattermole via Digitalmars-d

On 25/10/15 6:01 PM, Nerve wrote:

Hello D community! First time poster, I'm utterly fascinated with this
language's mix of features. It's powerful and expressive.

There are just two conveniences I'd like to see out of D. The first is
pattern matching, a functional construct which can unwrap tuples or
other containers, usually evaluates to a value (in most languages), and
which almost always idiomatically enforces the programmer to match over
every possible case of a given type.

While ML-inspired languages like F# and OCaml have some good pattern
matching syntax, it's not syntax which would fit in to D; I suggest
taking inspiration of Rusts's matching construct.

match x {
 Some(7) => "Lucky number 7!",
 Some(_) => "No lucky number.",
 None => "No value found"
}

 From that bit of code, we can see another feature at work: The Option
type. It wraps another type (i.e. Option int, Option Car) and represents
a wrapped presence of a value of that type (Some(n), Some(aCar)) or an
absence of that type (None).


Option = Varient


Combined with pattern matching, we end up with a safe, functional
construct which can replace a switch statement in most cases, returns a
value, is incredibly compact and readable, and can be used with Options
to ensure that we always account for the possibility of a value not
present, eliminating a whole class of errors when we use it judiciously.

My only concern is that switch statements, while horrendous
syntactically, are extremely performant and essentially compile to a
series of branches.

Are there any counter-arguments for the implementation of these two
features? Is D in a state where language additions have come to a stop?


So basically a little bit of magic for matching if having a value or not 
and switch statement.


Since I have no idea what the difference between Some(_), None and 
default. I'll assume it's already doable.


Re: Option types and pattern matching.

2015-10-24 Thread Nerve via Digitalmars-d
On Sunday, 25 October 2015 at 05:05:47 UTC, Rikki Cattermole 
wrote:
Since I have no idea what the difference between Some(_), None 
and default. I'll assume it's already doable.


_ represents all existing values not matched. In this case, 
Some(_) represents any integer value that is not 7. None 
specifically matches the case where no value has been returned. 
We are, in most languages, also able to unwrap the value:


match x {
Some(7) => "Lucky number 7!",
Some(n) => "Not a lucky number: " ~ n,
None => "No value found"
}

Or something to that effect. The equivalent switch statement 
right now would be:


if (x.hasValue()) {
switch (*x.peek!(int)) {
case 7:writeln("Lucky number seven!"); break;
default:   writeln("Not a lucky number: ", 
*x.peek!(int)); break;

}
} else {
writeln("No value.");
}

This does not return a value (is a procedural structure); the 
switch cannot match null; in order to unwrap, we must call peek() 
again; and between the extra if-else and the break statements, 
this is not as clean.


As a note, pattern matching could almost be considered an 
extended form of the ?: operator, which matches over value cases 
rather than boolean truthiness.


Apologies if this is all below you, I'm not in Andrei's or 
Walter's league, just an interested party trying to make 
suggestions to better the language.


Re: Option types and pattern matching.

2015-10-24 Thread Rikki Cattermole via Digitalmars-d

On 25/10/15 6:45 PM, Nerve wrote:

On Sunday, 25 October 2015 at 05:05:47 UTC, Rikki Cattermole wrote:

Since I have no idea what the difference between Some(_), None and
default. I'll assume it's already doable.


_ represents all existing values not matched. In this case, Some(_)
represents any integer value that is not 7. None specifically matches
the case where no value has been returned. We are, in most languages,
also able to unwrap the value:

match x {
 Some(7) => "Lucky number 7!",
 Some(n) => "Not a lucky number: " ~ n,
 None => "No value found"
}

Or something to that effect. The equivalent switch statement right now
would be:

if (x.hasValue()) {
 switch (*x.peek!(int)) {
 case 7:writeln("Lucky number seven!"); break;
 default:   writeln("Not a lucky number: ", *x.peek!(int)); break;
 }
} else {
 writeln("No value.");
}


I'm pretty sure e.g. opEquals/opCmp should work here.
Shouldn't need to switch upon a primitive type. Theoretically could do 
it on a e.g. struct. Which has the special comparison that you want.



This does not return a value (is a procedural structure); the switch
cannot match null; in order to unwrap, we must call peek() again; and
between the extra if-else and the break statements, this is not as clean.

As a note, pattern matching could almost be considered an extended form
of the ?: operator, which matches over value cases rather than boolean
truthiness.

Apologies if this is all below you, I'm not in Andrei's or Walter's
league, just an interested party trying to make suggestions to better
the language.


No no it's fine. Only this morning I was toying with the idea of 
variable length struct's on IRC. Turns out, wouldn't really work.