D needs to publicize its speed of compilation
H. S. Teoh
hsteoh at quickfur.ath.cx
Tue Jan 2 18:13:34 UTC 2018
On Tue, Jan 02, 2018 at 04:00:11PM +0000, Atila Neves via Digitalmars-d wrote:
> On Saturday, 30 December 2017 at 01:09:59 UTC, H. S. Teoh wrote:
[...]
> > Yeah... recently I've been resorting to `dmd -unittest -main
> > module.d` on single modules for faster turnaround time, because in
> > spite of D's famed compilation speeds, some things still make it go
> > far too slow, e.g. if you use template-heavy / CTFE-heavy code, or
> > there are just too many files to compile.
> >
> > [...]
>
> This is pretty interesting. Unfortunately I can see it getting messy
> with multiple dependent modules and/or if the function has "real"
> template parameters on top. I have to think about how to use your
> strategy in my own code.
[...]
Yeah, when you have dependent modules it can get pretty messy. Which is
why I've been moving towards modularising my code beyond what people
would usually do, i.e., turn even symbols in dependent modules into
template parameters so that you can make the module completely
independent. Here's one actual code example I'm particularly proud of:
------
/**
* Expands '@'-directives in a range of strings.
*
* Returns: A range of strings with lines that begin with '@' substituted with
* the contents of the file named by the rest of the line.
*/
auto expandFileDirectives(File = std.stdio.File, R)(R args)
if (isInputRange!R && is(ElementType!R : const(char)[]))
{
import std.algorithm.iteration : joiner, map;
import std.algorithm.searching : startsWith;
import std.range : only;
import std.range.interfaces : InputRange, inputRangeObject;
import std.typecons : No;
return args.map!(arg => arg.startsWith('@') ?
cast(InputRange!string) inputRangeObject(
File(arg[1 .. $]).byLineCopy(No.keepTerminator)) :
cast(InputRange!string) inputRangeObject(only(arg)))
.joiner;
}
unittest
{
static struct FakeFile
{
static string[][string] files;
string[] contents;
this(string filename) { contents = files[filename]; }
auto byLineCopy(KeepTerminator dummy) { return contents; }
}
FakeFile.files["file1"] = [
"x=1",
"y=2"
];
FakeFile.files["file2"] = [
"z=3"
];
import std.algorithm.comparison : equal;
assert([
"p=abc",
"@file1",
"q=def",
"@file2",
"e=ghi"
].expandFileDirectives!FakeFile.equal([
"p=abc",
"x=1",
"y=2",
"q=def",
"z=3",
"e=ghi"
]));
}
------
As you can see, I made File into a template parameter so that the
unittest can simulate a virtual filesystem with a struct, without ever
needing to deal with the messiness of actually creating and managing
temporary files on a real filesystem. This is (mostly) invisible to user
code (i.e., other code that uses this function) because if you don't
specify what File should be, it defaults to the usual std.stdio.File.
Note also that not all of File's API needed to be implemented; since the
function only uses .byLineCopy, that's all I needed to implement in my
surrogate FakeFile struct.
Not all code can be cut up in this way, though. I've only achieved this
in a few low-level modules; for other modules I'm still facing the
problem of having to specify dependent modules on the command-line. But
in trying to attain to this goal, it really forces you to think about
structuring your code in a better way than just throwing in haphazard
dependencies that may not strictly be necessary.
T
--
Just because you can, doesn't mean you should.
More information about the Digitalmars-d
mailing list