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 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: A collection of DIPs

2015-09-12 Thread Edmund Smith via Digitalmars-d

On Saturday, 12 September 2015 at 06:25:45 UTC, NX wrote:

On Friday, 11 September 2015 at 19:30:56 UTC, ponce wrote:
Some of us use and need @nogc all the time. The other parts of 
an application can use the GC: best of both worlds.


Is there even a compiler switch to disable GC altogether so the 
program doesn't have a GC thread? No, I can't see it...


There are a few levels of 'everything @nogc' you can go - make 
`main` @nogc but still have the runtime initalised, link to 
`_start @nogc` and intercept it so that it doesn't even start the 
runtimes, or (for minimising the binary/going bare metal) 
providing a custom null runtime and not linking the D runtime at 
all.


From when I tried to make the smallest PE hello world I could for 
2.066:


//Level 1
void main() @nogc
{
//Do stuff
}

//Level 2 - no runtime initialisation, runtime still there 
though

extern(C) void _start() @nogc
{
//Runtime not started yet - must be initialised before 
using much of the standard library

}

//Level 3 - no runtime, trick the linker (it expects runtime 
hooks)

extern(C)
{
@nogc:

//Custom runtime/moduleinfo stuff - not very portable in 
my experience - YMMV
//Provided so the linker doesn't have any 'runtimeFnHook 
missing' errors, but we don't actually include the runtime

__gshared void* _Dmodule_ref;
//Depending on compiler/version, these may have to exist 
for the linker

//__gshared void* _d_arraybounds;
//__gshared void* _d_assert;
//__gshared void* _d_unittest;
//May want to provide empty bodies instead if your linker 
can remove unused

// / garbage functions
void _d_dso_registry(void* ignore){}

void _start()
{
//Bare metal ready
}
}

I needed a few runtime-affecting compiler (and linker) switches 
too, depending on which compiler I was using, although it was 
mostly obvious stuff listed in `$(compiler) --help`.


Re: We need a typesystem-sanctioned way to cast qualifiers away

2015-06-21 Thread Edmund Smith via Digitalmars-d

First post, so let's see how this goes:

On Saturday, 20 June 2015 at 00:07:12 UTC, Andrei Alexandrescu 
wrote:
1. Reference counting: it's mutation underneath an immutable 
appearance. For a good while I'd been uncomfortable about that, 
until I figured that this is the systems part of D. Other 
languages do use reference counting, but at the compiler level 
which allows cheating the type system. It is somewhat fresh to 
attempt a principled implementation of both reference counting 
and safe functional collections, simultaneously and at library 
level.


My conclusion is that changing the reference count in an 
otherwise immutable structure is an entirely reasonable thing 
to want do. We need a way to explain the type system even 
though the payload is const or immutable, I'll change this 
particular uint so please don't do any optimizations that would 
invalidate the cast. The user is responsible for e.g. atomic 
reference counting in immutable data etc.


This idea makes sense with the type checker, although I'm not 
sure how it fits D's current memory model. On the page detailing 
const and immutable (dlang.org/const3.html) it mentions that 
immutable data 'can be placed in ROM (Read Only Memory) or in 
memory pages marked by the hardware as read only', which may be 
problematic if an immutable's mutable reference counter is placed 
in a read-only page. Would this potentially be changed? It 
suggests that the compiler needs a way of telling if the struct 
is a typical immutable struct or is privately-mutable, 
publicly-immutable.


As for the language level, it helps to look at how an immutable 
value propagates. It can be passed by value or by reference, 
passed between threads without issue and its value never changes. 
The thread safety strongly implies that what isn't immutable is 
shared, so that every field in an immutable struct is still 
thread-safe. Its exposed value never changing is also crucial 
IMO, to the point where I think it should be a compiler error if 
the 'facade' of immutability collapses (e.g. mutable data is 
public or modifies a public function's result).


Passing copies also raises a point about the current state of 
'immutable struct S', which has a few things different to normal 
structs (and block anything but POD types. Is this intentional?).
Currently, D lacks destructors and postblit constructors on 
immutable structs, giving the error 'Error: immutable method 
SomeStruct.__postblit is not callable using a mutable object'. 
Throwing an immutable qualifier makes it no longer recognisable 
as a postblit constructor, so that doesn't work either. This 
makes perfect sense under the current assumption that 'immutable 
struct' implies it is a POD struct, but if/when under-the-bonnet 
mutability goes ahead, this assumption will no longer hold (and 
postblit constructors are pretty useful with refcounting). The 
same goes for destructors, too. Without these, passing immutable 
refcounts by copy isn't safe - the copy keeps the pointer, but 
doesn't increase the counter. This leads to a potential 
use-after-free if the copy outlives every 'proper' reference.
I'm not sure whether this is all necessary, however - it is 
possible to just ignore 'immutable struct' and use 'alias 
MyStruct = immutable(MyActualStruct);', in which case the 
previous paragraph can be ignored.


After playing around with examples of how to do proper immutable 
reference counting (e.g. static opCall factory) I think the 
smallest change large enough to work with would be to allow 
transitive immutability to not transfer immutability to 'private 
shared' variables (or behave this way in the memory model, so 
that casting to mutable is safe but the type system remains 
entirely transitive), while simultaneously making it a 
compile-time error to read or write to these variables from 
exposed (public) functions. This may want to be loosened or have 
exceptions (debug builds etc.), depending on other use-cases.


'shared' seems to be used at the moment for a few different hacks 
(e.g. GDC having 'shared' and 'volatile' behave identically for a 
while) and nothing particularly concrete, save some library 
functions and inter-thread message passing. It also seems 
suitable for avoiding overeager compiler optimisations, since the 
'shared' qualifier already shows that typical assumptions can't 
be made about it. Also, most existing scenarios I can think of 
for a private shared variable in an immutable object are rather 
contrived. Finally, it should push the use of atomic code, or at 
least basic multithreading good practices.


The forcing public code to avoid touching the mutable variables 
enforces the idea that this is meant for 'systems' code, not 
typical implementation logic (it would be pointless to use it for 
this). It also prevents the mutable-ness from leaking, either 
through return values or other logic ('return (mutaBool)?1:0') 
and enforces a clean design that the systems code wraps around,