Re: Option types and pattern matching.
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.
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
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
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,