Cool features of the D language

12 05 2012

Since several weeks, I gave a second look at the D programming language after watching videos of talk gave by Andrei Alexandrescu and Walter Bright at Lang.NEXT conference (see Walter video and Andrei video). I decided to explain my favorite and cool features of the language. Of course there’s many more, I invite you to take a look at the website to find more information about the language.

scope guard statement

scope guard statement allows you to write code that will be executed either at the exit of the function, when the function succeeds (when they are no error or expection raised) and when you get failure by error or an exception.

It can be used to simulate the RAII pattern. Say you need to write data coming from a database into a File and the API isn’t RAII friendly. You could use try-catch-finally statements like this

auto file = new std.stream.File("test.txt", FileMode.In);
auto db = new SqlDatabase();

try
{
    db.open();
}
catch(SqlException e)
{
    writeln(e);
    return;
}

try
{
    db.callFunctionThatFails(file);
}
catch(SqlException e)
{
    writeln(e);
}
finally
{
    db.close();
    file.close();
}

It’s a simple code with some flaws (the File object is never closed).If we nest various try-catch statement, code can become pretty cluttered and hard to read. Here’s the version with the scope(exit) statement

auto file = new std.stream.File("test.txt", FileMode.In);
auto db = new SqlDatabase();

scope(exit)
{
    file.close();
    db.close();
}

try
{
    db.open();
    db.callFunctionThatFails(file);
}
catch(SqlException e)
{
    writeln(e);
}

Behind the scene, the compiler will write the correct try-catch statement for you.

The other scope guard are scope(success) and scope(failure). Here’s I created a small testcase with throw an exception every multiple of 3. It prints “Everything under control” and return 2 when there’s no exception and print “Just a flesh wound !” and return 5 when an exception is thrown.

import std.stdio;

int globalCounter = 0;

void functionThatThrowRandomly()
{
    globalCounter++;

    globalCounter = globalCounter % 3;

    if(globalCounter == 0)
    {
        throw new Exception("Forced exception");
    }
}

int test()
{
    scope(success)
    {
        writeln("Everything under control.");
    }
    scope(failure)
    {
        writeln("Just a flesh wound !");
        return 5;
    }

    functionThatThrowRandomly();

    return 2;
}

void main(string[] argv)
{
    foreach(i; 0..20)
    {
        writeln("Iteration ", i);

        int value = test();
        writeln("Value: ", value);
    }

    readln();
}

Here’s the output of this code:

Iteration 0
Everything under control.
Value: 2
Iteration 1
Everything under control.
Value: 2
Iteration 2
Just a flesh wound !
Everything under control.
Value: 5
Iteration 3
Everything under control.
Value: 2
Iteration 4
Everything under control.
Value: 2
Iteration 5
Just a flesh wound !
Everything under control.
Value: 5
Iteration 6
Everything under control.
Value: 2
Iteration 7
Everything under control.
Value: 2
Iteration 8
Just a flesh wound !
Everything under control.
Value: 5
Iteration 9
Everything under control.
Value: 2
Iteration 10
Everything under control.
Value: 2
Iteration 11
Just a flesh wound !
Everything under control.
Value: 5
Iteration 12
Everything under control.
Value: 2
Iteration 13
Everything under control.
Value: 2
Iteration 14
Just a flesh wound !
Everything under control.
Value: 5
Iteration 15
Everything under control.
Value: 2
Iteration 16
Everything under control.
Value: 2
Iteration 17
Just a flesh wound !
Everything under control.
Value: 5
Iteration 18
Everything under control.
Value: 2
Iteration 19
Everything under control.
Value: 2

More information about scope guard statement

templates and static if

D is influenced by C++ and contains similar features. One of the most powerful and confusing feature in C++ is template. Templates in C++ are used to create generic containers, algorithms, metaprogramming, compile-time type identification using traits and much more. If you are doing only generic container and algorithm, template are intuitive. But as soon you start doing metaprogramming, templates in C++ become a real clusterfuck.

In C++, to do a template that selects between two types according to a boolean expression looks like this (taken from Andrei’s book Modern C++ design)

template<bool flag, typename T, typename U>
struct Select
{
    typedef T Result;
};
template<typename T, typename U>
struct Select<false, T, U>
{
    typedef U Result;
};

template<typename T, bool isPolymorphic>
class NitfyContainer
{
    typedef typename Select<isPolymorphic,T*,T>::Result ValueType;
    ValueType m_value;
}

In D, using template declaration and static if, the same template looks like this

template Select(bool condition, T, F)
{
    static if (condition) alias T Select;
    else alias F Select;
}

class NitfyContainer(T,bool isPolymorphic)
{
    alias Select!(isPolymorphic,T*,T) ValueType;
    ValueType m_value;
}

First thing you notice is that D does not use the angular bracket for template like C++, Java and C#.  In C++, if you are using a C++03 compiler, type like map<string,Vector<int>> will confuse the compiler on >>, because it can’t decide if it’s a template or the shift right operator. (This problem was fixed in the C++11 standard). In D, they designed the language to be easy to comprehend by a tool with no or little semantic analysis as possible.

Another neat thing about template in D is if the variable inside the template has the same name as the template, you don’t need to access it, the compiler will use the value directly, as you can see in the Select example.

Variadic template are also supported (template that accepts arbitrary number of arguments)

template SelectFirstOrLast(bool selectFirst, Ts...)
{
    static if(selectFirst)
    {
        enum SelectFirstOrLast = Ts[0];
    }
    else
    {
        enum SelectFirstOrLast = Ts[$ -1];
    }
}

void test()
{
     auto firstItem = SelectFirstOrLast!(true,1,2,3,4,5);
     auto lastItem = SelectFirstOrLast!(false,1,2,3,4,5);

     // firstItem is 1
     // lastItem is 5
}

Variadic argument are accessed like an array: you can use the length of the array shortcut (the dollar sign), you can use slices and so on. Notice also the use of enum. In D, enum can be a compile-time constant of any type. It is not limited only to integer and the compiler can infer the type from the context.

More information about templates

More information about static if

Compile time function execution

D allows function to be called at compile time. Of course they are restrictions:

  1. The function source code must be available to the compiler.
  2. Executed expressions can’t reference global or local static variable
  3. Cannot call assembly code
  4. Non-portable cast
  5. C-style pointer arithmetic
  6. Non-recoverable errors (such as assert failures)
For functions to be evaluated at compile time, you must store the result of your function in a static variable, a const variable or an enum variable.
Here’s an example from Wikipedia that generate factorials into an array at compile-time
int[] genFactorials(int n) {
    auto result = new int[n];
    result[0] = 1;
    foreach (i; 1 .. n)
        result[i] = result[i - 1] * i;
    return result;
}

enum factorials = genFactorials(13);

// 'factorials' contains at compile-time:
// [1, 1, 2, 6, 24, 120, 720, 5_040, 40_320, 362_880, 3_628_800,
//  39_916_800, 479_001_600]

More info about compile-time function execution

mixin

mixin is like eval in most of the dynamic languages (Javascript, Python, Ruby), it takes a string literal and compile it to D.

void main(string[] args)
{
    mixin("auto x = 3.1415;");
    writeln("PI generated by mixin: ", x);
}

But as you learned in the last section, you can execute code at compile-time, including string concatenation. It opens many possibilities for code generation.

Philippe Sigaud’s Pegged, a parsing expression grammar (PEG) generator, is the quintessential example of mixin power. You define the grammar in a string literal

import pegged.grammar;

mixin(grammar(`
Arithmetic:
    Expr     <  Factor AddExpr*
    AddExpr  <  ^('+'/'-') Factor
    Factor   <  Primary MulExpr*
    MulExpr  <  ^('*'/'/') Primary
    Primary  <  '(' Expr ')' / Number / Variable / ^'-' Primary

    Number       Variable `));

Behind the scene, the grammar() template generated classes for each rule of the grammar. Here’s how to use the generated code by grammar()

// Parsing at compile-time:
enum parseTree1 = Expr.parse("1 + 2 - (3*x-5)*6");

pragma(msg, parseTree1.capture);
assert(parseTree1.capture == ["1"d, "+"d, "2"d, "-"d, "("d, "3"d, "*"d, "x"d, "-"d, "5"d, ")"d, "*"d, "6"d]);
writeln(parseTree1);

// And at runtime too:
auto parseTree2 = Expr.parse(" 0 + 123 - 456 ");
assert(parseTree2.capture == ["0"d, "+"d, "123"d, "-"d, "456"d]);

In my experiment with D, I was able to generate serialization code using templates, mixin, compile-time function execution and compile-time reflection. Here’s the full source code of that experiment, it doesn’t compile on ideone.com because the DMD compiler is too old.

More information about mixin

Uniform function call syntax

UFCS (as it’s called by the community) is a newest addition of the language that unifies the calling syntax. It allows free functions (functions outside classes) that take a class as a first parameter to be called like it was a member of that class,  similar to extension methods in C#. It’s ease the pain while using non-member functions to increase encapsulation, an idiom suggested by Scott Meyer in Effective C++.

Let’s say you don’t want your serialization code to clutter your class or struct definition, you could do something like this:

class MyClass
{
    // some fields and method
}

void serialize(MyClass p, JSONSerializer serializer)
{
    serializer.writeString(p.stringField);
    // etc...
}

void main()
{
    auto data = new MyClass();
    auto jsonWriter = new JSONSerializer();

    serialize(data, jsonWriter);
}

Without UFCS, you need to pass explicitly MyClass instance to serialize. But with UFCS, you can do instead.

data.serialize(jsonWriter);

The downside of UFCS is that it is harder to tell if the method is defined inside the class or if it’s a free function.

More information about UFCS

with

with is a simple statement inspired by Pascal and Visual Basic that simplify repeated calls or access to an object. It’s a nice feature to have when you interact with C code like the Win32 API which require lots of fields initialization.

Here’s a C code that register a window class in Win32


WNDCLASSEX wcex;
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 );
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"TutorialWindowClass";
wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_TUTORIAL1 );
if( !RegisterClassEx( &wcex ) )
   return E_FAIL;

And here’s the same block in D using the with statement


WNDCLASSEX wcex;
with(wcex)
{
    style = CS_HREDRAW | CS_VREDRAW;
    lpfnWndProc = &WndProc;
    cbClsExtra = 0;
    cbWndExtra = 0;
    hInstance = p_hInstance;
    hIcon = LoadIcon(p_hInstance, cast(const char*)IDI_TUTORIAL1);
    hCursor = LoadCursor(NULL, IDC_ARROW);
    hbrBackground = cast(HBRUSH)(COLOR_WINDOW+1);
    lpszMenuName = "";
    lpszClassName = "TutorialWindowClass";
    hIconSm = LoadIcon(p_hInstance, cast(const char*)IDI_TUTORIAL1);
}

Note that to remove ambiguity, I renamed hInstance parameter to p_hInstance.

More information about with statement

Conclusion

The D language is powerful and deep and they are many things to learn about this language. For go further, I suggest you read the official documentation on the website and to read Andrei’s book The D Programming Language. As for me, I think I’ve finally found a worthy successor of C++ for my projects that required C++ in the past. I’m currently investigating doing DirectX 11 code using D.

Advertisements

Actions

Information

7 responses

13 05 2012
philippesigaud

Nice article Michaël! And thanks for the link to Pegged.

About UFCS, I played with it this week and discovered it makes for some nice user code with a unit module (as in ‘SI units’, not ‘unittests’):

auto speed = 100.miles/hour;
auto distance = 350.km;
writeln(“time to destination: “, (distance/speed)/hour);

Not something essential in this case, but definitely nice to have.

13 05 2012
Cool features of the D language « (setf blog (import lib "thoughts"))

[…] https://michaellarouche.wordpress.com/2012/05/12/cool-features-of-the-d-language/ Share this:TwitterFacebookLike this:LikeBe the first to like this post. Posted in: programming | Tagged: D […]

13 05 2012
denisshel

Your serialization code is too complicated. There is no need to generate D code and use mixins for serialization. See this (works with static arrays, dynamic arrays, knows the difference between null and non-null empty arrays and remembers it):
http://ideone.com/cwNHv

13 05 2012
Denis

Good, but your serialization code is too complicated. The reader may think this is the right may to do serialization, not an example of code generation. Mention it please.
By the way, serialization code should look like this (it is old and may work only with dmd ~2.052, but do the job correctly (e.g. distinguish null arrays and non-null empty arrays) and works for both Windows and Linux):
http://ideone.com/cwNHv

17 05 2012
LuisCM

Great post!

LuisCM – D Consultant

2 01 2014
Ajinkya

How different is D language from Java? is there any good website to learn about D from start, I am thinking to learn along with my java learning by following>blogs and tutorials from internet

7 01 2014
michaellarouche

It is very different, Java is running in a virtual machine and D is a native language. As for learning, I recommended this online book: http://ddili.org/ders/d.en/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: