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

Reply via email to