Thread overview
Discriminated Unions
Nov 18, 2013
Stretto
Nov 19, 2013
deadalnix
Nov 19, 2013
bearophile
Nov 19, 2013
bearophile
Nov 19, 2013
Dejan Lekic
Nov 19, 2013
Timon Gehr
Nov 20, 2013
Philippe Sigaud
Nov 21, 2013
Stretto
November 18, 2013
F# now has Discriminated Unions:

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

Would it be possible to have something similar in D?
November 19, 2013
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/
November 19, 2013
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_expression_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
November 19, 2013
> 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
November 19, 2013
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 .
November 19, 2013
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)
    );
}
November 20, 2013
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?
November 21, 2013
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.