Discriminated Unions

bearophile bearophileHUGS at lycos.com
Mon Nov 18 16:40:07 PST 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


More information about the Digitalmars-d mailing list