In my last post, I gave a brief overview of my background and interest in exploring the D programming language from a background of a seasoned C# developer. (Also note that I feel all "curly bracket" family of languages developers would appreciate D, it's just that if C# is one of your personal favorite of the family, D may have even more for you to like.)..
Now for the good.
After enumerating so many points lacking in D, you may wonder why I'm so enamoured with it. The language itself has many features found in C#, and also many features C# lacks. In some case's D implementation of a feature is superior to D. Let's give a brief introduction to some of the really cool parts of D that have no counterpart, or aren't done as well, in C#.
Templates
C# has generics, but as I hope to explain in future posts, they're not nearly as powerful as what D can do. Anyone who does worked a lot with C++ templates and found C# generics lacking, would be pleasantly surprised with D. D brings back the power of C++ templates without all the awkward syntax and linking issues you have with C++. Templates are a concept in D somewhat orthogonal to the definition of a type or method. A template is just a parameterized piece of code. You can place class and method definition inside a template, then those classes and methods are templated.
public template ListTemplate(T)
{
public class List
{
void add(in T item)
{
//stuff
}
//etc
}
}
That illustrates the idea, but if all you really want is a old fashioned template class, D, being pragmatic, has a shortcut:
public class List(T)
{
void add(in T item)
{
}
}
In addition to classes and functions, you can declare variables and type aliases inside templates too.
You can have value template arguments as well, something that doesn't exist in C#.
public class SillyArray(int size)
{
int[size] _myArray;
}
Now, the size parameter is part of the type of declared variables. If you've never worked with template value parameters before, it might not seem obvious what you gain from them. Because it affects the type of the class, just like a type parameter would, you could do things like define a range type:
public class Range(int low, int high)
{
int _value;
this(int value)
{
assert(value >= low && value <= high);
_value = value;
}
}
Now you have a type where the legal range forms part of the type itself, and the compiler can distinguish the legality at compile time.
Let's define an alias, akin to C++'s typedef though more powerful, to reference the type:
alias liquidWater = Range!(32, 212);
Now later on we could define a variable,
auto myCup = new liquidWater(4); //error, out of range.
In this case, the compiler can determine that the assertion will fail, and so it flags it as a compiler error. This is another great point about the D compiler is that it will evaluate assertions at compile time when it has enough information! When it doesn't it will still evaluate it at run time.
C# does have a limited type constraint system for generic type parameters:
class foo<T> where T : IDisposable where T : new()
{
}
This lets you declare that what type is used to instantiate foo must implement IDisposable, and that it must have a parameterless constructor(this feature seems like an odd special case to place in the language, I always wondered why they stopped there).
In D, it gets waaaaaay cooler than that. Essentially any logic you can supply that can be determined entirely at compile time can be used as a type of value constraint.
Going back to our range type, lets say someone did something like this:
alias invertedRange = Range!(100, 0);
Notice how the "low" parameter was greater than the "high" parameter? As the designer of the Range class, you never intended that. How might you prevent someone from doing that in a way that it's caught at compile time?
You add an template constraint expression:
public class Range(int low, int high) if (low <= high)
{
int _value;
this(int value)
{
assert(value >= low && value <= high);
_value = value;
}
}
public class Range(int low, int high)
{
int _value;
this(int value)
{
assert(value >= low && value <= high);
_value = value;
}
}
Now you have a type where the legal range forms part of the type itself, and the compiler can distinguish the legality at compile time.
Let's define an alias, akin to C++'s typedef though more powerful, to reference the type:
alias liquidWater = Range!(32, 212);
Now later on we could define a variable,
auto myCup = new liquidWater(4); //error, out of range.
In this case, the compiler can determine that the assertion will fail, and so it flags it as a compiler error. This is another great point about the D compiler is that it will evaluate assertions at compile time when it has enough information! When it doesn't it will still evaluate it at run time.
Type constraints.
C# does have a limited type constraint system for generic type parameters:
class foo<T> where T : IDisposable where T : new()
{
}
This lets you declare that what type is used to instantiate foo must implement IDisposable, and that it must have a parameterless constructor(this feature seems like an odd special case to place in the language, I always wondered why they stopped there).
In D, it gets waaaaaay cooler than that. Essentially any logic you can supply that can be determined entirely at compile time can be used as a type of value constraint.
Going back to our range type, lets say someone did something like this:
alias invertedRange = Range!(100, 0);
Notice how the "low" parameter was greater than the "high" parameter? As the designer of the Range class, you never intended that. How might you prevent someone from doing that in a way that it's caught at compile time?
You add an template constraint expression:
public class Range(int low, int high) if (low <= high)
{
int _value;
this(int value)
{
assert(value >= low && value <= high);
_value = value;
}
}
Now the compiler will not allow the inverted range alias declaration. As long as the logic can all be determined at compile time, anything is possible as a constraint, even function calls!:
bool isLower(int a, int b)
{
return a < b;
}
public class Range(int low, int high) if (isLower(low,high))
{
int _value;
this(int value)
{
assert(value >= low && value <= high);
_value = value;
}
}
This syntax really gives you a rich language for expressing contracts between parts of code in ways that I have not seen in other languages. These are compile time contracts, validation is immediate, not through unit testing or function tests, but before you ever have executable code. Your intention as an API designer can be very explicit. That, is what make me smitten with the language.
I''ve barely scratched the surface of what you can do with D's generic programming system. If you want to know more, check out the very well written online book "Programming in D" by Ali Çehreli at http://ddili.org/ders/d.en/templates.html
I''ve barely scratched the surface of what you can do with D's generic programming system. If you want to know more, check out the very well written online book "Programming in D" by Ali Çehreli at http://ddili.org/ders/d.en/templates.html
Until next time,
--P
No comments:
Post a Comment
I welcome you're thoughts. Keep it classy, think of the children.