Dolce and Free Functions

posted in D Bits
Published May 22, 2011
Advertisement
There's one thing that's missing from D that I really wish it had: namespaces. There have been discussions about this on the D newsgroup in the past. Walter Bright, D's daddy, has taken the position that D's modules *are* namespaces. In a way, they are. But, not really.

For those new to the party, D eliminates the need for header files. Interface and implementation are all part of the same code unit, called a module. Instead of #including headers, you import modules. Modules can reside in a package hierarchy, much like Java classes. So, given this source file:


// This is the module 'bar' in the package 'foo'.
module foo.bar;

void baz()
{
// Do something.
}


You would use it like this:

module myapp.mymod;

// Import the foo.bar module so its symbols are accessible by this module
import foo.bar;

void myfunc()
{
// baz is visible, so it can be called like this
baz();

// Or, with the fully-qualified name
foo.bar.baz();
}


So yes, a module namespace does exist, but it isn't enforced by default. That means that there can be conflicts if multiple imported modules export functions/types with the same signature. Now, from the user side, it can be enforced two ways. First, is by using static import.

static import foo.bar;

Now, everything you access in the foo.bar module must be prefixed with "foo.bar". You can go one better and add an alias that let's you call foo.bar by another name:

alias foo.bar Foobar;

This allows you to call Foobar.baz(), but it's still foo.bar.baz() that is being enforced under the hood. Remove the static keyword from the import, and baz() will still be accessible by itself. The alias is just an alternative, not an enforced namespace.

The second approach is to use a feature called "named imports", which effectively combines the two steps above, but with enforcement of the given name instead of the module name.

import Foobar = foo.bar;

Now, everything you access in the foo.bar module must be prefixed with "Foobar".

These approaches are all fine, but they put the onus on the user to create his own namespace. As a solution for solving namespace conflicts in client code, I think they're just peachy. In fact, I really like them. But from the perspective of a library designer, I have one major issue with it. I want to wrap my functions in a namespace, so that the client can't have conflicts to begin with and doesn't have to remember to use a named or static import.

Currently, in Dolce, I'm presented with the dilemma of how to handle certain functions that really ought to be free functions. I really don't want to take the C approach with function naming conventions, because it just doesn't mesh with the parts of the library that aren't free functions. On the other hand, I don't want to pollute the global namespace with function names that could easily clash with other libraries or client code. One option is to use static methods in a struct, or a non-instantiable class. But, to me, that just really feels dirty. Plus, every new type you define in a D program adds a TypeInfo instance to the final executable. It's not a lot, but it's just useless bloat for a class that is serving as a namespace hack. It offends my sensibilities, I suppose.

So one more option that I'm considering is having a sort of wrapper module that publicly imports the modules with free functions, giving them a name.

module foo.bar;

public import Bar = foo.realbar;


Imported symbols, by default, are accessible only in the module that imports them. So if module A imports module B, and you then import module A , you can't access B's symbols. However, if module A publicly imports B, then you can access B's symbols just by importing module A. So this enforces the namespace on the client if they import this module. Furthermore, clients will have a choice. They can import foo.bar and get access to the Bar namespace, or they can import foo.realbar and get the free functions, or create their own namespace if they need it. I only see a couple of drawbacks. The only problem I have is deciding if this really improves the situation or not.

Of course, the other option is just not to worry about it. Phobos is full of free functions, after all. Ultimately, my point of view is that I'm creating this library for myself, so whatever makes me happy is the way I'm going. I just have to decide which approach makes me happy.
Previous Entry To Wrap or Not To Wrap
Next Entry Game Screens
0 likes 6 comments

Comments

Aldacron
[quote name='AndrejM' timestamp='1306180160']
Hey, not sure if you knew about this already, but you can kind of simulate things if you use static imports and [b]with[/b],
[/quote]

The with statement doesn't help me here. That would be useful if I wanted to simulate the C++ [b]using[/b] statement as a library user.

What I want is to enforce the use of a namespace from the library side. D has no feature that allows that. static imports and named imports all rely on the client to implement.

[quote]And then users would always import [b]dolce.all[/b].[/quote]

Never been a fan of this idiom in D. You'll never see a foo.all module in any of my projects!

[quote]Here's another technique, instead of having one all module you could have separate modules and let the user decide which one to import. So you'd have something like this:[/quote]

Yes, this is what I meant in the last couple of paragraphs. It's the approach I actually settled on and implemented, but only for the modules containing free functions. I thought it would be the right solution, but I'm really not that happy with it. It doesn't actually solve anything. The onus is still on the user. But now, instead of deciding whether or not to use a static/named import, they just decide which module they want to import.

So I've finally decided that it just doesn't matter. I've gotten rid of the wrapper modules. Dolce will have some free functions with no namespaces. If there are any clashes, users can solve it themselves with a static or named import and I won't worry about it.

[quote]
Nice blog to follow btw. Good luck with your project!
[/quote]

Thanks!
May 24, 2011 12:29 AM
immutable
Maybe i can help you:

You create a template:
[code]

const string _type_check = "if (is(T == string) && !is(T == A)) return false;";

template Vector() {
bool contains(T, A...)(const T[] array, const A elements) {
mixin(_type_check);

if (elements.length > array.length) {
return false;
}

foreach (T e; elements) {
if (array.position(e) < 0) {
return false;
}
}

return true;
}

int position(T, A)(const T[] array, const A target) {
foreach (uint index, T e; array) {
if (e == target) {
return index;
}
}

return -1;
}
}
[/code]


And use it with:
[code]

import namespace;

import std.stdio;

mixin Vector V;

void main() {
int[] numbers = [1, 2, 3, 4, 5, 6, 8, 7];

if (V.contains(numbers, 1)) {
writeln("enthält");
}
}
[/code]


If you prefer the c++ equivalent "using namespace ...", you must use "mixin Vector;" this allows you to have access to all functions without namespace prefix.
June 15, 2011 11:00 AM
immutable
What do you want? Something like in C++, where by default you only can call the functions if you call the functions with the namespace Prefix?
June 15, 2011 10:07 PM
Aldacron
Yeah, that's it. I want to provide free functions in a namespace and force the user to call through the namespace by default. Right now, the only way to simulate this, that I know of, is to declare a class or struct with static methods. That serves the purpose fine, but is, again, a hack. Plus, it creates a TypeInfo for every struct and class you declare. Not a big deal, but those TypeInfo objects are never going to be used and are just taking up space. So I know how to work around it. I just would really like to have real namespaces in D so a workaround wouldn't be necessary.
June 16, 2011 01:08 PM
immutable
Yes name spaces would are a nice feature. But i didn't think, that Walter creates something like this for D.
And how want you to handle your problem now?
Something i hate in D, is that the parameter storage classes are added to the template types. But thats a other story^^
June 16, 2011 03:49 PM
Aldacron
Right, namespaces aren't likely to ever be added. I'd just like to have them, that's all. For now, I just keep the functions free.
June 18, 2011 06:55 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement