www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Discriminated Unions

reply "Stretto" <uiy12345 gmail.com> writes:
F# now has Discriminated Unions:

http://msdn.microsoft.com/en-US/library/dd233226.aspx

Would it be possible to have something similar in D?
Nov 18 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 18 November 2013 at 23:55:50 UTC, Stretto wrote:
 F# now has Discriminated Unions:

 http://msdn.microsoft.com/en-US/library/dd233226.aspx

 Would it be possible to have something similar in D?
http://www.deadalnix.me/2013/05/10/type-safe-tagged-union-in-d-programming-language/
Nov 18 2013
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Stretto:

 F# now has Discriminated Unions:

 http://msdn.microsoft.com/en-US/library/dd233226.aspx

 Would it be possible to have something similar in D?
Unless I am missing something, they seem the regular algebraic types you find in Haskell, etc. A little example program in Haskell (from https://github.com/Dobiasd/articles/blob/master/from_oop_to_fp_-_inheritance_and_the_e pression_problem.md ): import Data.List data Base = Foo Int | Bar String step :: Base -> Int -> Base -- Add delta to internal state. step (Foo intVal) delta = Foo $ intVal + delta -- Concat delta as string to internal state. step (Bar strVal) delta = Bar $ strVal ++ show delta display :: Base -> String display (Foo intVal) = show intVal display (Bar strVal) = strVal -- Steps every object in l by 1. stepAll :: [Base] -> [Base] stepAll l = map (\b -> step b 1) l -- Displays all objects in l beneath each other. displayAll :: [Base] -> IO () displayAll l = putStrLn $ concat (intersperse "\n" $ map display l) main = let -- Fill a list with "derived instances". l :: [Base] l = [Foo 0, Bar ""] -- Step every object two times. l' = (stepAll . stepAll) l in -- Show result. displayAll l' In D with OOP: import std.stdio, std.conv, std.algorithm; interface Base { void step(in int delta) pure; string display() const pure; } class Foo: Base { this(in int i) pure nothrow { this.intVal = i; } /// Add delta to internal state. override void step(in int delta) pure nothrow { intVal += delta; } override string display() const pure { return intVal.text; } private int intVal; } class Bar: Base { this(in string s) pure nothrow { this.strVal = s; } /// Concat delta as string to internal state. override void step(in int delta) pure { strVal ~= delta.text; } override string display() const pure nothrow { return strVal; } private string strVal; } alias BaseArr = Base[]; /// Steps every object in items by 1. void stepAll(BaseArr items) pure { foreach (o; items) o.step(1); } /// Displays all objects in items beneath each other. void displayAll(in BaseArr items) { writefln("%-(%s\n%)", items.map!(o => o.display)); } void main() { // Fill a vector with base class pointers to derived instances. //BaseArr l = [new Foo(0), new Bar("")]; BaseArr l = [new Foo(0)]; l ~= new Bar(""); // Step every object two times. l.stepAll; l.stepAll; // Show result. l.displayAll; } In D immutable: import std.stdio, std.algorithm, std.variant, std.conv, std.array; alias Base = Algebraic!(int, string); Base step(in Base b, in int delta) { if (auto bi = b.peek!int) return Base(*bi + delta); // Add delta to internal state. else if (auto bs = b.peek!string) return Base(*bs ~ delta.text); // Concat delta as string to internal state. assert(0); } string display(in Base b) { if (auto bi = b.peek!int) return text(*bi); else if (auto bs = b.peek!string) return *bs; assert(0); } // Steps every object in l by 1. Base[] stepAll(in Base[] l) { return l.map!(b => b.step(1)).array; } // Displays all objects in l beneath each other. void displayAll(in Base[] l) { writefln("%-(%s\n%)", l.map!display); } void main() { // Fill a list with "derived instances". immutable l = [Base(0), Base("")]; // Step every object two times. const l2 = l.stepAll.stepAll; // Show result. l2.displayAll; } How it could become with some improvements in Algebraic (it defines an enum of the types, usable in a final switch): import std.stdio, std.algorithm, std.variant, std.conv, std.array; alias Base = Algebraic!(int, string); Base step(in Base b, in int delta) { final switch (b.type) with (b) { case intType: return Base(b.get!int + delta); case stringType: return Base(b.get!string ~ delta.text); } } string display(in Base b) { final switch (b.type) with (b) { case intType: return b.get!int.text; case stringType: return b.get!string; } } // Steps every object in l by 1. Base[] stepAll(in Base[] l) { return l.map!(b => b.step(1)).array; } // Displays all objects in l beneath each other. void displayAll(in Base[] l) { writefln("%-(%s\n%)", l.map!display); } void main() { // Fill a list with "derived instances". immutable l = [Base(0), Base("")]; // Step every object two times. const l2 = l.stepAll.stepAll; // Show result. l2.displayAll; } This is much worse than the code you can write in Rust, but perhaps it's still usable. Bye, bearophile
Nov 18 2013
parent "bearophile" <bearophileHUGS lycos.com> writes:
 Base step(in Base b, in int delta) {
     final switch (b.type) with (b) {
         case intType:    return Base(b.get!int + delta);
Or better: final switch (b.type) with (b.Types) { Bye, bearophile
Nov 18 2013
prev sibling next sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
On Tue, 19 Nov 2013 00:55:48 +0100, Stretto wrote:

 F# now has Discriminated Unions:
 
 http://msdn.microsoft.com/en-US/library/dd233226.aspx
 
 Would it be possible to have something similar in D?
It is in Phobos under the name Variant : http://dlang.org/phobos/ std_variant.html .
Nov 18 2013
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/19/2013 12:55 AM, Stretto wrote:
 F# now has Discriminated Unions:

 http://msdn.microsoft.com/en-US/library/dd233226.aspx

 Would it be possible to have something similar in D?
Sure. DMD still refuses to compile my proof of concept implementation though: https://d.puremagic.com/issues/show_bug.cgi?id=10431 https://d.puremagic.com/issues/show_bug.cgi?id=11558 In essence, the following is easily possible in theory: mixin ADT!q{ List(T): | Nil | Cons T List!T }; auto list(R)(R r) if(isInputRange!R){ if(r.empty) return Nil!(ElementType!R); auto f = r.front; r.popFront(); return Cons(f,list(r)); } size_t length(T)(List!T l){ return l.match!( ()=>0, (x,xs)=>1+length(xs) ); }
Nov 19 2013
parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Tue, Nov 19, 2013 at 11:31 PM, Timon Gehr <timon.gehr gmx.ch> wrote:

 In essence, the following is easily possible in theory:

 mixin ADT!q{
  List(T):
  | Nil
  | Cons T List!T
 };

 auto list(R)(R r) if(isInputRange!R){
     if(r.empty) return Nil!(ElementType!R);
     auto f = r.front;
     r.popFront();
     return Cons(f,list(r));
 }

 size_t length(T)(List!T l){
     return l.match!(
         ()=>0,
         (x,xs)=>1+length(xs)
     );
 }
I concur, I did it also a few years ago. IIRC, I created an abstract base class (List) and different subtypes. The associated matching function was also generated at the same time. What did you use to encode the types in your case? A tagged union?
Nov 20 2013
prev sibling parent "Stretto" <uiy12345 gmail.com> writes:
On Monday, 18 November 2013 at 23:55:50 UTC, Stretto wrote:
 F# now has Discriminated Unions:

 http://msdn.microsoft.com/en-US/library/dd233226.aspx

 Would it be possible to have something similar in D?
<ALL> ...I was thinking more of a built in way to do this but I guess these solutions will do.
Nov 21 2013