Remix.run Logo
Features of D That I Love(bradley.chatha.dev)
193 points by vips7L 2 days ago | 221 comments
elcritch 2 days ago | parent | next [-]

> Syntax - UFCS (Uniform Function Call Syntax)

UFCS is such an underrated language feature. When you have UFCS you can toss out 90% of the uses of methods in favor of just plain ole functions. Add generic functions and concepts and you rarely end up needing OO support.

pjmlp a day ago | parent | next [-]

OO support is more than just record.method(), which is something you don't even have in OOP systems based in multi-dispatch.

sirwhinesalot a day ago | parent [-]

I always find it funny how people focus on the .method() syntax. You have the "pipe" operator in some functional languages that achieves the same thing, pass the result of an expression as the first argument of the chained call on the right. Nothing to do with OOP.

pjmlp a day ago | parent | next [-]

I think it needs a higher level experience across procedural, logical, functional and object based languages, the unique ways each one applies ideas into their programming model, how that interacts to CS, for finally understanding how those concepts come together, instead of being so fixated in language syntax.

xigoi 17 hours ago | parent | prev [-]

This is because in languages that don’t have UFCS, methods are often abused to create “natural” looking syntax like

    expect(6).toBe(even)
kmarc a day ago | parent | prev | next [-]

Reminds me of vim script's implicit method syntax [1]

Eg. any function call can be converted to a method call on the function's first parameter:

    let mylist = [3, 2, 1]
    " prints "1" as these two are equivalent
    echo sort(mylist) == mylist->sort()
Helps a lot with chaining.
kyleee 9 hours ago | parent [-]

Quirky and awesome

kmarc 2 hours ago | parent [-]

If you think about it, it's the mirrored version of python object/class methods: they receive the object (self) / class (cls) as the first parameter. Same with Rust's trait impl's methods.

layer8 2 days ago | parent | prev | next [-]

Dot syntax tends to work better for code completion, though.

In addition, without uniform call syntax, adding a new method can only break subclasses, whereas with uniform call syntax it can break other client code.

Doxin a day ago | parent [-]

There's nothing preventing UFCS from working with code completion. e.g. given:

    int addOne(int v){
        return v+1;
    }
You can now write code like this:

    int foo=3;
    writeln(foo.addOne);
There is absolutely no reason that typing "foo." would not suggest "addOne" as possibility.
layer8 a day ago | parent [-]

My comment was about the reverse, using function syntax for methods.

Furthermore, I don’t think it necessarily makes sense for all functions that happen to take, say, a string as their first argument, to be listed in the code completion for method invocation on a string variable.

If you merely want to define auxiliary methods outside of a class, which is the thing the GP seems to like, that’s what’s usually called “extension methods”. It doesn’t require uniform call syntax.

Doxin a day ago | parent | next [-]

> using function syntax for methods

hmm, yeah fair enough I suppose. I don't think I've found a good use-case for that yet. I guess having the symmetry there makes the feature easier to explain at least? I dunno.

> Furthermore, I don’t think it necessarily makes sense for all functions that happen to take, say, a string as their first argument, to be listed in the code completion for method invocation on a string variable.

All functions in scope that happen to take a string as their first argument. If this turns into an actual problem in practice it's quite doable to refactor things such that it's not an issue.

I find that when I use autocomplete I'll be typing the first bit of the method name in any case. I never "browse" autocomplete to look for the thing I need.

Extension methods are another way to do the same thing yes, but that feels like special-casing behavior, where UFCS is more general. With extension methods you need to think of how to implement it wrt things that don't usually have methods attached. With UFCS that just works the way you'd expect it to.

jasperry a day ago | parent | prev [-]

I agree with this. I'd go even further and say that dot syntax should only be used to access things that are /actually a part of the object/, whether record fields or methods. If you use the dot for everything just because it's convenient, you're making the code structure harder to understand by syntactically conflating different mechanisms.

zem 20 hours ago | parent [-]

unless your intent is to simulate open classes, and the functions you call via the dot are conceptually meant to be an extended set of methods for the type

motorest a day ago | parent | prev | next [-]

> When you have UFCS you can toss out 90% of the uses of methods in favor of just plain ole functions.

Can those go away if you use inheritance or polymorphism, and you need your functions to access protected or private members? I mean, OO is as much about methods as functional programming is about functions.

zozbot234 a day ago | parent [-]

Implementation inheritance is generally a misfeature, especially in modern languages with proper generics. If you insist on having it, you can replicate it manually by using interface inheritance and the generic typestate pattern.

motorest 21 hours ago | parent [-]

> Implementation inheritance is generally a misfeature (...)

Not really. It's value proposition is code reuse. It's not a misfeature just because it breaks a simple explanation.

zozbot234 20 hours ago | parent | next [-]

You only ever need interface inheritance for code reuse. The genuine value proposition for implementation inheritance is, quite unsurprisingly, the same as for typestate and generic typestate, namely better type checking within a single self-contained program module.

Implementation inheritance "in the large" remains a total misfeature and should not be used, other than for very tightly defined, extensible "plug in" architectures (where the point of extensibility can itself be treated as a single module). But these are really quite rare in practice.

motorest 19 hours ago | parent [-]

> You only ever need interface inheritance for code reuse.

Not really. Interfaces play no role in invoking member functions, even if they are defined in a base class. Inheritance is used to allow member functions declared in a parent class to be called in a member functions without requiring boilerplate code. Instead of having to duplicate code, inheritance provides a concise way to specify a) this is what I want to reuse, b) this little thing is what I want to change in the implementation.

> The genuine value proposition for implementation inheritance is, quite unsurprisingly, the same as for typestate and generic typestate, namely better type checking within a single self-contained program module.

No. The value proposition is not requiring any boilerplate code to extend or change any detail in a base class. With inheritance, you just declare the thing you want to add or change, and you do not need to touch anything else. The alternatives being floated fail to meet very basic requirements such as visibility, encapsulation, and access control.

> Implementation inheritance "in the large" remains a total misfeature and should not be used (...)

Not true. This is a personal belief based on specious reasoning. You need to go way out of your way to ignore the problems that inheritance solved while ignoring the negative impact of the alternatives being floated.

zozbot234 19 hours ago | parent [-]

> Inheritance is used to allow member functions declared in a parent class to be called in a member functions without requiring boilerplate code.

The defining characteristic of implementation inheritance is open recursion. When you call a "member function declared in a parent class" there's no telling what code will actually be run, because that member function may have been overridden at any point in the class hierarchy. There's no way of knowing whether the caller and callee code will agree on the required semantics and invariants involved, or even what these should be for any given case. That's why this is a misfeature for a "programming in the large" scenario.

By contrast, these issues can be managed when using implementation inheritance within a self-contained, smaller-scale program module, and then its open recursion behavior, properly managed, matches what we expect from the use of the typestate pattern.

galangalalgol 20 hours ago | parent | prev | next [-]

I don't find code reuse to be an unqualified virtue. The most hideous codebases I have been involved with all share an obsession with DRY that destroyed them. Most of them were also really obsessed with extensibility which didn't help. So while implementation inheritance can certainly help with code reuse, I consider that an anti-feature.

elcritch 20 hours ago | parent | prev [-]

You get about as much reuse from a set of re-usable functions as you get from inheriting implementations.

Though I grant that having an object hierarchy does make it a bit more explicit what’s being inherited or needs implementing. However, a OO hierarchy also tends to obscure the actual parent implementations as well. Just having a set of functions from a module generally lowers the number of indirections.

In general I find working through a non-OO code base generally easier to grok and understand. Especially the OP culture from Java or C# style OO, even if I’m generally good at understanding OO patterns.

galangalalgol 20 hours ago | parent [-]

Ok, this is what I was trying to say in my sibling post, but without the snark and including the why.

girvo a day ago | parent | prev | next [-]

I was about to say "Yeah, its my favourite feature of Nim!" and then I realised what account I was replying to ;)

elcritch 20 hours ago | parent [-]

Haha, Nim is my preferred UFCS language.

jayd16 2 days ago | parent | prev [-]

If all you're doing is accessing public members, sure.

nicwilson 2 days ago | parent | next [-]

`private` is only private to the module, not the struct/class, (In other words, all functions in the same module are all C++ style `friend`s) and so free function in same module work.

WalterBright 2 days ago | parent | prev [-]

It works for all functions that have parameters, `f(a)` and `a.f()` are equivalent.

jayd16 2 days ago | parent [-]

Yes, but I presume that f() cannot access private members of a.

WalterBright 2 days ago | parent [-]

That's correct.

eric-p7 2 days ago | parent | prev | next [-]

It's a mystery why D isn't far more popular than it is. Fast compilation, familiar syntax, and supports a wider range of programming paradigms than most (any?) other language.

dataflow a day ago | parent | next [-]

> It's a mystery why D isn't far more popular than it is.

There's no mystery. It's a jack of all trades, master of none. E.g., the virality of the GC makes it a non-starter for its primary audience (C/C++ developers). The need to compile and the extra verbosity makes it a bad substitute for scripting like Python. Etc.

Basically, name any large niche you'd expect it to fill and you'll probably find there's a tool already better suited for that niche.

bachmeier a day ago | parent | next [-]

> the virality of the GC makes it a non-starter for its primary audience (C/C++ developers)

No. If you were to say you need the GC to use all features of the language and standard library, of course, the GC does important things, but to claim a C developer wouldn't be comfortable with it because of the GC is nonsense. Just don't allocate with the GC and use the same mechanisms you'd use with C (and then build on top of them with things like @safe, reference counting, and unique pointers).

dataflow a day ago | parent | next [-]

>> the virality of the GC

> Just don't allocate with the GC

"virality" is not just a word you can ignore.

bachmeier a day ago | parent [-]

I don't understand. If you're a C programmer and want to avoid the GC, there's nothing to be viral.

dataflow a day ago | parent [-]

> If you're a C programmer and want to avoid the GC, there's nothing to be viral.

What you're suggesting is the moral equivalent of "it's easy to avoid diseases, just avoid contact with those infected", or "it's easy to avoid allergens, just avoid foods you're allergic to", or "it's easy to avoid contamination, just set up a cleanroom", or "it's easy to write deterministic code, just avoid randomness", etc.

Yes, there are things that are easy to achieve in the vacuum of outer space, but that's not where most people are interested in living.

brabel a day ago | parent | next [-]

Completely agree after trying it. Anything that may throw an Exception requires GC. There goes 80% of D code you could use. The rest becomes inaccessible for other, similar reasons quite often. Try writing D with @nogc and it takes 10 minutes to understand that. They want to make the situation better but there’s just not enough people to tackle the huge amount of work that requires (I.e. changing most of the stdlib)

dataflow a day ago | parent | next [-]

> Try writing D with @nogc and it takes 10 minutes to understand that.

Thank you. Yes, exactly. The problems aren't even subtle; it's impossible to miss them if you actually try. I don't recall even finding a reasonable way to concatenate or split strings on the heap and return them without a GC, let alone anything more complicated. It boggles my mind that people repeat the talking point that it's somehow practical to program with @nogc when the most basic operations are so painful. Nobody is going to drool at the idea of spending days/weeks of their lives reinventing nonstandard second-class-citizen counterparts to basic types like strings just to use a new language.

> They want to make the situation better but there’s just not enough people to tackle the huge amount of work that requires (I.e. changing most of the stdlib)

I don't agree that it's lack of manpower that's the problem -- at least, not yet. I think it's primarily the unwillingness to even admit this is a problem (an existential problem for the language, I think) and confront it instead of denying the reality, and secondarily the inertia and ecosystem around the existing language outside the standard library. It's not like the problem is subtle (like you said, a few minutes of coding makes it painfully obvious) or novel. The language has been out there for over a decade and a half, and people have been asking for no-GC version nearly that long. Yet, at least to the extent I've had the energy to follow it, the response has always been the canned you-can-totally-program-D-without-a-GC denials you see repeated for the millionth time here, or (at best) silence. If this sentiment has changed and I'm unaware of it, that's already significant progress.

Maybe the unwillingness to confront reality is due to the lack of manpower and how daunting the problem looks; I'm not sure. But it seems as bright as daylight that D is not going to be successful without solving this problem.

WalterBright a day ago | parent | next [-]

I use:

https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ba...

It's pretty minimalist on purpose. I don't much care for kitchen sink types.

The BetterC is the no-gc version. Use the -betterC switch on the compiler.

Or, if you want a string result,

    import core.stdc.stdlib;

    string concat(string s1, string s2)
    {
        const length = s1.length + s2.length;
        char* p = cast(char*)malloc(length);
        assert(p);
        p[0 .. s1.length] = s1;
        p[s1.length .. s1.length + s2.length] = s2;
        return cast(string)p[0 .. length];
    }
I tend to not use this sort of function because it doesn't manage its own memory. I use barray instead because it manages its memory using RAII. D provides enormous flexibility in managing memory. Or, you can just leave it to the gc to do it for you.
dataflow a day ago | parent [-]

> I use: https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ba... It's pretty minimalist on purpose. I don't much care for kitchen sink types.

I feel you're demonstrating exactly the problems I highlighted through your example here -- including the very lack of acknowledgment of the overall problem.

WalterBright 14 hours ago | parent [-]

The problem is there is no such thing as a string type that doesn't have problems one way or another.

The very simplest and straightforward way is to use the gc to manage the memory. It works very very well. All the other schemes have serious compromises.

That's why you can use the method most appropriate in D for the particular usage. I routinely use several different methods.

Zardoz84 a day ago | parent | prev [-]

Part of the problem it's trying to do too many things in too many fronts. They try to implement a borrow-checker a la Rust. But feels very poorly compared against the Rust version. It haves a "optional" GC, but it's a subpar GC. And lacks a way to use alternative GCs.

And funny, C++ has been copying many features that DLang have for many time ago. Beginning with the type inference (ie using "auto" to declare vars). And now, contractual programing and static reflection.

I really loved the language, but it's very sad that never manages to take off and become more popular and well maintained.

gpderetta 20 hours ago | parent | next [-]

> Beginning with the type inference (ie using "auto" to declare vars).

GCC (and possibly other compilers) had typeof in the '90s well before D was first released. Macros in the form:

   #define    TYPEOF(name, expr) typeof(expr) name = expr
Were widely in use since then.

I'm sure that C++ borrowed concepts from D, but type deduction (not inference BTW) is not one of them.

pjmlp a day ago | parent | prev [-]

That has been the main problem from my point of view, too much pivoting looking for the right crowd, without finalizing what was done before.

And while some features in other languages might have seen an implementation first in D, claiming first to the finish line as it usually comes up, even on this thread, hardly does anything for language adoption.

On the contrary, it is one reason less leave those languages, as eventually they get the features, and already have the ecosystem.

bachmeier 21 hours ago | parent | prev [-]

This completely misses the point of my original comment. A C programmer that wants to continue to use their knowledge and existing code will be very happy using D. You're describing someone that wants to use D without the garbage collector. Those are two completely different cases.

I've been happily using D for a better experience with C code for more than a decade. First, because it's extremely rare to need to completely avoid the GC for everything in your program. Second, because everything you want and need from C is available, but you can use it from a more convenient language. Sure, exceptions won't work if you're avoiding the GC (which doesn't have anything to do with C), but so what. It's not like C programmers are currently using exceptions. You can continue to use whatever mechanism you're using now.

zem 20 hours ago | parent [-]

> Sure, exceptions won't work if you're avoiding the GC (which doesn't have anything to do with C), but so what. It's not like C programmers are currently using exceptions.

that works if your main use case for d is as a top-level wrapper program that is basically calling a bunch of c libraries. if you want to use d libraries you will run into ones that need the gc pretty quickly.

bmacho a day ago | parent | prev [-]

According to them[0], D as better C is indeed just C with less footguns and some additional optional features like RAII (that one can use or not) or more comptime assumptions (again, that one can use or ignore).

I don't think what hinders their adoption is their direction, everything they say they accomplished/plan to accomplish is ideal IMO.

[0] : https://dlang.org/spec/betterc.html#retained

dataflow a day ago | parent [-]

> D as better C is indeed just C with less footguns and some additional features like RAII (that one can use or not) or more comptime assumptions (again, that one can use, or ignore)

Having strictly more features (if we even assume that, which I don't think is accurate) does not imply better.

Javascript is just JSON with more features too. Is it a mystery that people don't ship Javascript code everywhere instead of JSON?

unclad5968 a day ago | parent | prev [-]

If I'm just coding C except a new syntax, why wouldn't I just stick with C?

dataflow a day ago | parent [-]

It's not just new syntax, you get other nice features too.

WalterBright a day ago | parent | prev | next [-]

With D you don't have to use multiple languages, such as mixing Python with C. D is The One Language to Rule Them All.

(D's ability to use C code is to make use of existing C code. There's not much point to writing C code to use with D.)

dataflow a day ago | parent | next [-]

> D is The One Language to Rule Them All.

That's kind of why I said it's the "jack of all trades". It's not a bad language, it just doesn't beat any existing languages I know of in their own niches (hence "master of none"), so few people are going to find it useful to drop any existing languages and use D in lieu of them.

jibal a day ago | parent [-]

It's amusing to see someone telling the designer and implementer of a language that he has put decades of effort into that "it's not a bad language".

WalterBright 14 hours ago | parent | next [-]

I often hear a lot worse :-) No worries, it doesn't bother me. What pleases me are the people who use D, like it, and tell me they make more money using D because D is much more productive to write code in.

D also attracts expert programmers who are very comfortable using the GC when appropriate, stack allocation when appropriate, malloc/free, even ref counting. These are just tools in the toolbox, like I use socket wrenches, end wrenches, box wrenches, crow foot wrenches, pipe wrenches, monkey wrenches, etc. I don't try to use socket wrenches for everything.

BTW, the GC makes managing memory in compile time function execution trivial. Something that non-GC languages struggle with.

dataflow 12 hours ago | parent [-]

> What pleases me are the people who use D, like it, and tell me they make more money using D because D is much more productive to write code in.

I guess what I've been trying to say is that you would find yourself pleased much, much more often (and D being much more successful) if you recognized and addressed these high-level issues that people have been pointing out for decades, instead of denying them and going on forums telling customers why their expectations are wrong or unnecessary. I'm saying this because D really is a great piece of technology that got a lot of things right, except a few crucial details for some of the most crucial users. And it has had so much potential - potential that has been gradually lost largely because you haven't even recognized the flaws and hurdles that come with it.

It remind me of the infamous Dropbox comment. It's as if you invented FTP, but then whenever people told you it's hard to store & share files, you kept insisting that it's trivial with just a few simple steps on Linux, completely missing the massive market opportunity and the barriers you're telling people to walk through. https://news.ycombinator.com/item?id=9224

And I'm not saying all this out of out hate for D, but out of (at least past) love for it. I desperately wanted to see it succeed, but I gave up because I realized you simply did not see the Achilles heel that frustrates many of its users and that has held back its potential.

WalterBright 8 hours ago | parent [-]

Anyone is free to propose things for D, we have a process for it, or you can just post your idea in the D forum. Many do. You don't have to sit back and hope someone else does it.

tialaramex a day ago | parent | prev [-]

It's better than the feedback I would have for, to give an example, Bjarne Stroustrup. Bjarne has spent so far as I can tell almost all of his adult life on C++. It's a huge bloated mess, and though there are many other guilty parties I don't think I can even say he was a good influence.

quietbritishjim a day ago | parent | prev [-]

One of C++'s great weaknesses is that it is just a huge language with too much stuff in. There are lots of reasons why this is, not worth re-exploring, but the point stands.

There is a great irony that a replacement to C++ should have lots of features in it. (Not necessarily the same too-many features.) One of the key requirements of a real C++ alternative would be fewer language features.

pjmlp a day ago | parent | next [-]

It is telling that Ada and Rust are only ones that many people in the C++ community would ever consider.

Because at the scale many companies use C++, the additions into ISO C++, for how bad WG21 process currently might be, don't land there because a group of academics found a cool feature, rather some company or individual has seen it as a must have feature for their industry.

Sadly also a similar reason on how you end up with extension spaghetti on Khronos APIs, CSS levels or what have you.

Naturally any wannabe C++ replacement to be taken seriously by such industries, has to offer similar flexibility in featuritis.

quietbritishjim a day ago | parent [-]

> Because at the scale many companies use C++, the additions into ISO C++, for how bad WG21 process currently might be, don't land there because a group of academics found a cool feature, rather some company or individual has seen it as a must have feature for their industry.

Maybe it's just me but, sorry, I cannot parse this sentence.

trealira 21 hours ago | parent [-]

I'm not them, but I'll try reordering the sentence to help you parse it better.

As bad as the WG21 process might be, the additions into ISO C++ don't land there because a group of academics found a cool feature; they land there because some company or individual has seen it as a must-have feature for their industry.

quietbritishjim 21 hours ago | parent [-]

Thank you :-)

feelamee a day ago | parent | prev [-]

Why is a big number of features a problem? You can ignore them if you don't like/need them. Can you briefly explain which features can be thrown out and language will not miss a lot without them?

quietbritishjim a day ago | parent [-]

> Why is a big number of features a problem? You can ignore them if you don't like/need them.

You 100% cannot do that. I mean, you can if you're writing some toy project just for your own use. But as soon as you start interacting with other programmers, it's inevitable that some will use some other subset of language features.

> Can you briefly explain which features can be thrown out and language will not miss a lot without them?

I don't think that it's controversial that C++ is a huge language with many features, and I doubt I'm the best person to rehash that. One often quoted example is the multitude of ways to initialise a variable (Foo x = y; Foo x(y); Foo x = {y}; Foo x{y} and for default initialisation Foo x; Foo x = {}; Foo x{}; Foo x() (not really - that's the most vexing parse); Foo x = Foo()). There's multiple syntaxes to define a function including auto and decltype(auto) return types. There are const, consteval and constexpr - you may know the difference but I've forgotten. There are so many templating features that I wouldn't know where to start. Concepts are layered on top of that - which are useful and a good idea but no denying that it's layering extra complexity on top (which can be said for many C++ features). I've really just scratched the surface.

The thing is, I learned C++ over 20 years ago, when the latest standard was C++03 (which was essentially the same as C++98). Even at the time, C++ seemed like a bit of a chunky language (e.g., compared to C or Object Pascal - languages tended to be simpler back then), but it was achievable to mostly understand it all. But each revision that passed has added a huge volume of features. So I really feel how big C++ is because it's even big compared to (an older version of) itself. I've mostly kept up over the years but I can't imagine how I would properly learn the language today from scratch - I feel like you don't really stand a chance unless you've also been closely following it for decades.

GoblinSlayer a day ago | parent | prev | next [-]

When you use many languages, don't you become a jack of all trades, master of none? Also it's not obvious python is good for scripting, bash is better at short scripts, statically typed languages are better at long scripts, and you can't use it in CI yaml scripts. Python is more famous for data science and AI, not for scripting.

dataflow a day ago | parent | next [-]

I used "scripting" loosely (Python source files are frequently called Python scripts), I wasn't referring to shell scripts specifically. Feel free to pretend I said Bash or data science or whatever you want.

Re: your first sentence: I neither understand the logic nor do I understand how insulting the developer is going to help D succeed here even if the logic was sound.

GoblinSlayer 21 hours ago | parent [-]

You handle technology as an emotional being? Well, that's strange. I thought it's more about tradeoffs.

bravetraveler a day ago | parent | prev [-]

Python is useful for scripts that go beyond strings. For instance: making a series of API calls, parsing/processing/mutating.

That's not data science or AI; "more famous" -- ridiculous distinction.

Fun fact: Ansible is orchestrated Python. Half your Linux distribution of choice is a pile of Python scripts. It's everywhere.

zem a day ago | parent | prev | next [-]

it should at the very least fit the same niche golang does

dataflow a day ago | parent | next [-]

I almost mentioned Go as another example of what it doesn't substitute for, but decided to cut off the list. But no, I don't think it fits that niche either. Go has a lot of features D doesn't have. Just off the top of my head: it's very simple, it doesn't have pointer arithmetic (yes, that's a feature here), it performs escape analysis to automatically extend the lifetime of objects that escape the stack frame, etc.

WalterBright a day ago | parent [-]

D does not allow pointer arithmetic in code marked @safe.

D does escape analysis from an alternative direction. If a pointer is qualified with `scope`, the compiler guarantees it does not escape the stack frame.

dataflow a day ago | parent [-]

I'm well aware of those, and they obviously don't fill the gaps here.

globular-toast a day ago | parent | prev [-]

Well that shouldn't be a mystery. Golang was marketed by Google.

p0nce a day ago | parent | prev [-]

The full quote is: “Jack of all trades master of none, though oftentimes better than master of one.”

sltkr a day ago | parent | next [-]

That's not “the” full quote; it's a modern variation of the classic quote.

Chinjut a day ago | parent | prev [-]

There is no documented instance of your "full quote" prior to the 21st century. It's certainly not the origin of the phrase "Jack of all trades, master of none".

giancarlostoro a day ago | parent | prev | next [-]

I needs a "Django", "Rails", "ASP .NET" type of project that is out of the box ready. Go has net/http out of the box. I think if D had either a batteries included (Vibe.d is nice, but you have to handle some things still) web framework, it might convince some folk to use it more.

Go spread the way it did because it was dead simple to make websites and services, the syntax is insanely simple, but the language allows you to scale without going through a ton of hoops. Look at how much of our IT infrastructure is powered by Go now.

D has a lot of potential, but someone has to sit down and build frameworks and tooling for D.

I think if D had officially supported libraries by core maintainers that take full advantage of the best of D it might be a different landscape.

Another area where D could shine is GUIs. Everything is electron these days, it feels like nobody builds usable GUI stacks. If D had an official solution to this, and it worked nicely and gave you enough power to customize your UI, we might see a shift there too. I welcome a Electron free future.

Look at the Zed editor (ignore the AI buzz) and how insanely fast it is. Its coded in Rust, and uses WGPU iirc to just render everything kind of like a video game, but it runs insanely fast. It is my new favorite text editor.

Sadly despite my deep love of D, Go is where I'm leaning more towards, due to industry pull there.

zem 19 hours ago | parent [-]

> Another area where D could shine is GUIs

I've often thought that, to the extent that I spent a while looking for some active projects I could contribute to, and came up blank. if I do have some new gui based program of my own I want to write I will at least consider d for it, though ocaml is another great language in the same space and I already have some experience with ocaml/gtk. my hope was that d would have more mature gui toolkit bindings and more of a community of people writing apps, which would have been some incentive to switch over from ocaml, I was disappointed to find that wasn't the case.

giancarlostoro 12 hours ago | parent [-]

I think it needs to do build its own GUI stack and not rely on any others, maybe take inspiration from tooling like Delphi's which allowed you to add themes on a whim.

zem 7 hours ago | parent [-]

I don't know, gtk has solved a lot of problems that you don't necessarily want to reinvent from scratch and the api is pretty consistent with an object oriented language

WalterBright 2 days ago | parent | prev | next [-]

We don't have a marketing budget, although we have many hard core users!

johnisgood 2 days ago | parent | next [-]

Is that really it? Why cannot you get a marketing budget, sponsored perhaps?

BTW:

I am not fond of stuff like:

  // Sort lines
  import std.stdio;
  import std.array;
  import std.algorithm;

  void main()
  {
      stdin
          .byLine(KeepTerminator.yes)
          .uniq
          .map!(a => a.idup)
          .array
          .sort
          .copy(stdout.lockingTextWriter());
Are there any ways to do this that do not involve a bunch of "."s? I do not understand "map!" and "a.idup" either, FWIW.

I really want to like D, but it tries to do too many things all at once, in my opinion.

Perhaps I will give C3 a fair try.

WalterBright 2 days ago | parent | next [-]

That code is an example of f(a) being equivalent to a.f(). You can do it the f(a) way if you prefer.

`map` is an operation on a data structure that replaces one element with another, in this case `a` gets replaces with `idup(a)`. The `idup` makes a copy of its argument in memory, and marks the data is immutable.

sureglymop a day ago | parent | next [-]

So it's basically a pipe or like the pipe operator in some languages? Looks interesting.

JdeBP a day ago | parent | next [-]

It has a fairly direct analogy in some languages. A C♯ programmer reading the above will immediately recognize the analogy to .Select(), .ToArray(), and so forth from LiNQ.

* https://learn.microsoft.com/en-gb/dotnet/api/system.linq.enu...

* https://learn.microsoft.com/en-gb/dotnet/api/system.linq.enu...

WalterBright a day ago | parent | prev [-]

Yup, it looks and behaves very much like Unix piping.

a day ago | parent [-]
[deleted]
johnisgood a day ago | parent | prev [-]

> That code is an example of f(a) being equivalent to a.f(). You can do it the f(a) way if you prefer.

How would it look like with this particular code? Just for comparison.

> The `idup` makes a copy of its argument in memory, and marks the data is immutable.

How is one supposed to know this? Reading the documentation? I really want to look at the code and be able to know straight away what it does, or have a rough idea.

zdragnar a day ago | parent | next [-]

See https://en.m.wikipedia.org/wiki/Uniform_function_call_syntax - the examples section has two side by side. The Nim language version is also pretty readable even if you're not familiar with it.

As for idup... The first several search results for "dlang idup" are all useful.

> I really want to look at the code and be able to know straight away what it does, or have a rough idea.

I presume you really don't like perl, ML based (ocaml, f sharp, rust) Haskell or K.

johnisgood a day ago | parent [-]

I love Perl[1] and OCaml. I dislike the rest. It depends on the task.

[1] https://news.ycombinator.com/item?id=44359539

> As for idup... The first several search results for "dlang idup" are all useful.

Yes, I am sure it was, I am sure an LLM would have helped too, but I think that is besides the point here.

zdragnar 18 hours ago | parent [-]

Is it beside the point? Looking at that perl example:

$string =~ s/\d+/NUM/g;

I don't have a clue what is going on. Sure, I see the regex, but what is =~ doing?

There's only so far you can stretch most languages before you need to actually put in effort to learn them.

johnisgood 2 hours ago | parent [-]

"=~" is the operator testing a regular expression match. It should be obvious, because on the right side you see regex, and you ought to know what "=" or "==" does.

FWIW, I knew this as a kid, too, despite knowing absolutely nothing about the language at the time.

Anyways, you should read https://news.ycombinator.com/item?id=44463391 if you care about why I dislike the way D does it.

p0nce a day ago | parent | prev | next [-]

I don't think you're wrong, I have used D for 19 years and don't really use the UFCS dot style, it requires too much context. Just loops are more readable. In D you don't have much decisions forced on you tbh.

johnisgood an hour ago | parent [-]

I appreciate it.

spacechild1 a day ago | parent | prev | next [-]

> Reading the documentation?

Yes?

> I really want to look at the code and be able to know straight away what it does, or have a rough idea.

You said elsewhere that you love Perl. Would you say your sentence above applies to Perl?

johnisgood a day ago | parent [-]

It does not, but Perl is a much older language, and I have familiarized myself with it through actual discourses regarding its syntax, implementation details, and so forth. But in this thread? All I got was wrongful accusations and down-votes. I am done replying to this thread. Should have just let Walter reply, but that was not enough for you people, was it?

Funny though, because most of the things these people accuse me of are dead wrong, and my comment history is proof of that. In fact, I have been down-voted to oblivion for telling people to read the documentation. I guess we may have come full circle.

Glad we had this utterly pointless chat.

spacechild1 17 hours ago | parent [-]

I'm not a D user and I don't have a ball in this game. I just found your critique of D's syntax rather odd because it can be applied to basically any language one is not already familiar with. Maybe I'm wrong, but I don't think my response was particularly rude...

johnisgood an hour ago | parent [-]

I mean, to me it just looks ugly. Perhaps written in a different way, it would have been better. I have seen Haskell and Elixir code, but they were much less ugly.

Then again, it is entirely subjective, and we should not argue about taste.

To each their own.

quietbritishjim a day ago | parent | prev [-]

> > That code is an example of f(a) being equivalent to a.f(). You can do it the f(a) way if you prefer.

> How would it look like with this particular code? Just for comparison.

I do not know how to write D, so the following might not compile, but it's not hard to give it a go:

    copy(sort(array(map!(uniq(byLine(stdin, KeepTerminator.yes)), a => idup(a)))), stdout.lockingTextWriter())
> > The `idup` makes a copy of its argument in memory, and marks the data is immutable.

> How is one supposed to know this? Reading the documentation? I really want to look at the code and be able to know straight away what it does, or have a rough idea.

Are you serious? You are offended by the idea of reading documentation?This is not helping the credibility of your argument. Again, I'm not a D user, but this is just silly.

johnisgood a day ago | parent [-]

> Are you serious? You are offended by the idea of reading documentation?This is not helping the credibility of your argument. Again, I'm not a D user, but this is just silly.

If you knew me, and you read my comment history, you would have NEVER said that. It is not even a matter of reading the documentation or not. "idup" seems arbitrary, sorry, I meant the whole line sounds arbitrary. Why "a"? Why "a.idup"? Why "map!"? I was asking genuine questions. You do not have to bash me and see ghosts. I was curious as to why it was implemented the way it was.

I am an active speaker against people who hate reading the documentation.

And FYI, I love Perl[1] and OCaml[2].

[1] https://news.ycombinator.com/item?id=44359539

[2] You would have to check the comment history.

schveiguy 21 hours ago | parent | next [-]

> Why "a"

`a` is a parameter in the lambda function `a => a.idup`.

> Why "map!"

This is definitely something that can trip up new users or casual users. D does not use <> for template/generic instantiation, we use !. So `map!(a => a.idup)` means, instantiate the map template with this lambda.

What map is doing is transforming each element of a range into something else using a transformation function (this should be familiar I think?)

FWIW, I've been using D for nearly 20 years, and the template instantiation syntax is one of those things that is so much better, but you have to experience it to understand.

> "idup" seems arbitrary

Yes, but a lot of things are arbitrary in any language.

This name is a product of legacy. The original D incarnation (called D1) did not have immutable data as a language feature. To duplicate an array, you used the property `dup`, which I think is pretty well understood.

So when D2 came along, and you might want to duplicate an array into an immutable array, we got `idup`.

Yes, you have to read some documentation, not everything can be immediately obvious. There are a lot of obvious parts of D, and I think the learning curve is low.

johnisgood an hour ago | parent [-]

Thanks for the reply.

> Yes, but a lot of things are arbitrary in any language.

I disagree, but to each their own.

> Yes, you have to read some documentation, not everything can be immediately obvious.

I do not disagree, but I wanted to know the rationale behind it ("map!(a => a.idup)")!

quietbritishjim a day ago | parent | prev | next [-]

I think perhaps you are not realising the negative tone of your comments. There is no way to read "How is one supposed to know this? Reading the documentation?" except sarcasm. No amount of good faith in unrelated comment threads changes this. I believe that's why you're getting downvoted - not because people are easily offended about D, as you seem to believe.

johnisgood a day ago | parent [-]

Well, I did not intend my statement to be sarcastic. It was a genuine question. Blame my lack of social skills, or the fact that I am on the spectrum. I was curious about the implementation details, i.e. why "!" (in map), why "a", why "idup", etc. That is not to say I am reluctant to read the documentation, I am more than willing, but I wanted to know the story behind it. I have ideas, but they might be wrong. I do not want to guess when I can have a direct answer from Walter.

WalterBright 8 hours ago | parent [-]

a!arg is used because I hated the look of a<arg> used in C++. (Using < > to bracket things when < means less than and > means greater than is always trying to short-circuit my brain.)

cxr 18 hours ago | parent | prev | next [-]

> I am an active speaker against people who hate reading the documentation.

How is one supposed to know this?

johnisgood 16 hours ago | parent [-]

I dunno, check my comment history if you are so curious. Been down-voted for daring to say that people should be less reluctant to read documentation.

cxr 16 hours ago | parent [-]

I refer you to the context of this discussion—how it is we got to be here:

> How is one supposed to know this? Reading[…]?

johnisgood an hour ago | parent [-]

Are you asking me to find the comment where I stated that I "dislike" people who are reluctant to read documentation?

a day ago | parent | prev [-]
[deleted]
johnisgood a day ago | parent | prev | next [-]

Look, I will not comment on D any further because it seems like it gets down-voted for no reason. Take your positive comments if you so wish, no need to down-vote the ones that express different opinions. You won. I will not be commenting on it any further. I shall NEVER be expressing my opinions on this language, nor will I ever raise any questions regarding the motivation for its implementation details.

People are too quick to use the "down-vote" button, and are too quick to judge. I love documentation, I write them. I am an active speaker against people who hate reading the documentation. This was not a case against reading documentation, yet people - wrongfully - believed so. People always glance past things like: "not fond of", and "in my opinion". It is tiresome.

This thread could have been educational, but instead it was a thread meant to bash me. It is my fault.

vips7L a day ago | parent | prev [-]

You don’t like instance functions?

johnisgood a day ago | parent [-]

I do not like this in particular:

  stdin
        .byLine(KeepTerminator.yes)
        .uniq
        .map!(a => a.idup)
        .array
        .sort
        .copy(stdout.lockingTextWriter());
I would like to emphasize that this is a personal preference. No need to continue to bash me over it.

I prefer Elixir's |> operator, if you want an example of something I prefer.

vips7L 21 hours ago | parent | next [-]

What’s the difference?

    stdin
      |> byLine(yes)
      |> uniq
      |> map(a => aidup)
      |> array
      |> sort
      |> copy(stdout)

I’m sorry if you took it as bashing. It’s mere curiosity as I’ve never seen that preference before.
johnisgood an hour ago | parent | next [-]

For what it's worth, in Elixir you might write something along the lines of the following (this is a rough translation, I haven't tested it):

  IO.stream(:stdio, :line)
  |> Stream.map(&String.trim_trailing/1)
  |> Enum.uniq()
  |> Enum.map(&String.duplicate(&1, 1))
  |> Enum.sort()
  |> Enum.each(&IO.puts/1)
This is not equivalent in style or presentation to:

  stdin
      .byLine(KeepTerminator.yes)
      .uniq
      .map!(a => a.idup)
      .array
      .sort
      .copy(stdout.lockingTextWriter());
Personally, I find the D version visually unappealing (and confusing), especially the way "stdin" sits alone on its own line, followed by a sequence of indented method calls. The excessive use of dots combined with the indentation structure makes it look, to me, rather awkward.

That is just my own opinion.

johnisgood 19 hours ago | parent | prev | next [-]

Is it a fair comparison? Would it work in Elixir? I have not seen it in Elixir projects as such.

Jtsummers 17 hours ago | parent [-]

Adjusting for the actual Elixir functions, yes that would work. That's how Elixir's |> works, it takes the value from the left and passes it as the first argument to function on the right. Which is what the chain of calls in D is doing.

johnisgood an hour ago | parent | next [-]

I have not yet seen such long chains in Elixir. Could you show me a project where it is used?

"map(a => aidup)" caught me by surprise, too. Would Elixir do such a thing?

vips7L 6 hours ago | parent | prev [-]

Thanks. I don’t know elixir.

18 hours ago | parent | prev [-]
[deleted]
amiga386 a day ago | parent | prev [-]

And yet... Perl is good with its chained operators acting on lists?

    print join '',
        sort { $a cmp $b }
        grep { !$seen{$_}++ } # = uniq
        <STDIN>;
johnisgood 33 minutes ago | parent | next [-]

Oh and by the way, it does not look that different from that of Rust. :) I have came across a lot of Rust projects.

johnisgood 19 hours ago | parent | prev [-]

I do not write such Perl code, but I have in the past.

ziml77 18 hours ago | parent | prev [-]

I wish you did have the marketing. I first read about D 10+ years ago and I continue to be disappointed that it never really took off. Having those hardcore users is great, but a language really is best with a thriving community and ecosystem. Languages like Go and Rust had the benefit of being able to kickstart those by having the backings of Google and Mozilla.

Keyframe 2 days ago | parent | prev | next [-]

I spent quite a few year on/with it back in the day. There was D1 which was like a better C, and then there was D2 which was like a better C++. Personally I preferred where D1 was going (and Tango instead of Phobos) but even with D2 it really made the day compared to what was out there and to this day still to an extent is. The thing that killed it for me, and I know at least a couple of friends as well (outside of internal politics at a time) was what kills pretty much all exotics once you start using it. Lack of (up-to-date) libraries / bindings and tooling. At the end of the day that's what you do use for most of the work you're doing anyways - libraries. So suddenly you're doing all these bindings and battling tools instead of working on actual problem at hand. This gets tiresome real quick.

For some reason, and mostly that being Mozilla, Rust got quite an initial kick to overcome that initial hurdle in haste. We're not going to mention a lot of those libs are stale in Rust world, but at least they're there and that kind of gives you momentum to go forward. Whatever you're trying to do, there's a non-zero chance there's a library or something out there for you in Rust.. and we got there real quick which then encouraged people to proceed.

That's just like my opinion, man.. but I think a key part is that first lib bindings hurdle which Rust somehow went over real quick for a critical mass of it; D hasn't.

Love the D though lol, and Walter is a 10000x programmer if you ever saw one but it might be time to hang the hat. I can only imagine how a community like Rust or I don't know Zig of those up-and-coming would benefit from his help and insights. He'd probably single-handedly make rust compile 100x faster. One can hope.

mamcx 2 days ago | parent | next [-]

Stressing the point, Rust ship very early with formatter, linter, cargo, rustup, and was not that behind in terms of editor support.

That is basically table stakes for a new language now.

skocznymroczny 16 hours ago | parent | prev | next [-]

I liked D1 more than D2 too. D1 felt to me like a native C#, D2 feels to me more like C++ with GC. And I feel like there's been a lot of effort to satisfy C++ programmers, who will never invest into D anyway because they don't like the idea of GC. The ship has sailed, but I think D would get more traction amongst the Java/C# crowd instead of chasing the C++ crowd which is mostly going for Rust now anyway.

jadbox a day ago | parent | prev | next [-]

I've heard nice things about Zig being a more ergo alternative to Rust, but I haven't seen anyone compare it to D yet. From my brief testing, it seemed like Zig wasn't as ergo as D, but in theory it could evolve to maybe get there. From the outside, it doesn't seem like Zig has made any super major ergo improvements in the last year, but I could be wrong.

pjmlp a day ago | parent | next [-]

Being a Modula-2 like safety language with curly brackets, plenty of @ and .{ } characters already puts it off for me.

For me any alternative to Rust implies having automatic resource managment, eventually coupled with improved type system, in a mix of affine types, linear types, effects or dependent types.

Something that in regards to safety is already available today by using GCC's Modula-2 frontend, FreePascal and similar, is not bringing too much to the table, comptime notwithstanding.

wavemode a day ago | parent | prev [-]

I don't know if I would describe Zig as ergonomic per se. It has some nice features, but the main focus is on completely explicit control over low-level details. In its design Zig always chooses explicitness and programmer control over ergonomics. If a language feature requires a lot of compiler magic then it's probably never going to be added.

josephg a day ago | parent [-]

Yeah I think of Zig as a modern version of C. And Rust and (I think) D like modern versions of C++. Well. Given D's marriage to the GC, maybe its more like Go or C#.

I know its petty - I still can't get past how idiotic and frustrating it is that Zig treats unused variables as a compiler error. Its the worst of all worlds:

- Its inconvenient (I have to explicitly suppress them in my code with _ = foo)

- Once I've suppressed them, I don't get any compiler warnings any more - so ironically, it takes more effort to find and fix them before committing. I end up accidentally committing code with unused variables more than in Rust or C.

- And it totally breaks my flow. I like to explore with my hands and run my code as I go. I clean up my code after my tests pass so I can be sure I don't introduce new bugs while refactoring.

Zig's handling of unused variables seems like an unforgivably bad design choice to me. Its been raised by the community plenty of times. Why hasn't it been fixed? I can understand if Andrew Kelly doesn't program the same way I do. We all have our idiosyncrasies. But does he seriously not have any smart people around him who he trusts who can talk him out of this design?

It seems like a huge pity to me. It otherwise seems like a lovely language.

kristoff_it a day ago | parent [-]

> Zig's handling of unused variables seems like an unforgivably bad design choice to me. Its been raised by the community plenty of times. Why hasn't it been fixed?

Because yours is just an opinion. It's perfectly legitimate to not like unused variable errors, but its factually wrong to say that no one wants it. You're just yucking somebody else's yum.

> But does he seriously not have any smart people around him who he trusts who can talk him out of this design?

He does, most of them also like unused variable errors. For what it's worth, I do too.

defen 20 hours ago | parent | next [-]

The unused variables thing seems like it's downstream of "no warnings, only errors". That is, once you're committed to errors-only, then unused variables are either an error, or not. And if those are your only two choices, then errors sound better.

However, I've written a decent amount of Zig code and that's probably my biggest complaint. Zig has put a ton of effort into making an ultra-fast developer experience with very low iteration times, and it's amazing. But then when I'm refactoring some code or trying to figure stuff out by, for example, commenting out some lines of code, I might get a bunch of unused variable errors. And so I spend more time fixing those than I do even compiling the code itself!

One thing I've seen suggested is using the linter to automatically insert `_ = foo` for unused variables, but I don't love that either because then what even is the point of the error in the first place?

But like I said that's all downstream of the no-warnings policy. And I totally understand the failure mode of warnings - I've worked on plenty of large projects that had 4,000 warnings and everyone ignored them and the actually useful ones would be invisible. Is there some middle ground where, I don't know, Debug builds can have warnings, but Release builds don't?

josephg 14 hours ago | parent [-]

I’d go further and say that zig already respects / trusts developers enough to manage their own memory. Philosophically it’s already not a language for sloppy teams who would ignore 4000 compiler warnings if given half a chance.

Zig is a language that demands a lot of rigour of the programmer. It offers a lot of trust. Far more so than Go or even Rust. It’s in light of that philosophy that it seems so weird. The compiler trusts me to manually manage my memory, but it’ll scold me like a naughty child if I ignore an unused variable for 5 minutes? Pick a lane.

I’d love to hear some arguments in support of this choice. The closest I’ve heard is “it doesn’t bother me, personally” - which isn’t a very strong argument.

I’m a little tempted to fork the compiler just to fix this. Can’t be that hard, right?

josephg a day ago | parent | prev [-]

In that case, can you help me understand the logic behind it?

The more time I spend thinking about it, the more convinced I am that its strictly worse. What am I missing? Why do you like it?

18 hours ago | parent [-]
[deleted]
steveklabnik 2 days ago | parent | prev [-]

I spent time back in the day with D as well, incidentally. I wonder if we crossed paths back then.

Keyframe 2 days ago | parent [-]

for sure we did, Steve! Sometimes multiple times a day even, hah. Check out ‪@keyframe2 on bsky or @keyframe on the evil platform and let's reconnect.

steveklabnik 2 days ago | parent [-]

Ha, that was so long ago I can barely remember a lot of it. I’ll give you a follow!

nicoburns 2 days ago | parent | prev | next [-]

I'd say that the compiler not being open source during the period when it might otherwise have become popular is probably a pretty big factor.

destructionator 2 days ago | parent [-]

The D parts of the compiler were released under the GPL from almost the beginning, since 2002. By 2004, a full open source compiler - what we now call gdc, officially part of gcc - was released using this GPL code. D was pretty popular in these years.

LugosFergus 18 hours ago | parent | prev | next [-]

Lack of sufficient tooling support was my issue. Debugging and autocomplete was a flaky mess in VSC. The plugin for VS pro wasn’t much better.

I did not like DUB at all. Its default behavior was to not segregate artifacts by configuration, and trying to change that was a headache.

It’s too bad, though. It’s a nice language, but I can’t see it making any inroads at this point.

mrkeen a day ago | parent | prev | next [-]

Articles like this I guess. If these are the 'lovable' features I'd hate to see the 'meh' features.

Automatic constructors - You only have to write the 'make me a box of two apples' code and not 'this is how two apples go into a box'! This is as revolutionary as 'automatic function calls', where you don't have to manually push the instruction pointer to pop it back off later.

Parenthesis omission!

If I were to parody this I'd talk about how good Scala is - in addition to classes, you can also declare objects, saving you the effort of typing out the static keyword on each member.

Sell me something nice! Millions of threads on a node. Structured concurrency. Hygienic macros. Homoiconicity. Higher-kinded types. Decent type inference. Pure functions. Transactions.

WalterBright a day ago | parent | next [-]

D has pure functions:

https://dlang.org/spec/function.html#pure-functions

D's pure functions are quite strict. It can be a challenge to write a function that passes strict purity guarantees - but the result is worth it!

zem 19 hours ago | parent | prev [-]

compile time function evaluation is "nice" in the same way those features are, isn't it?

GZGavinZhao a day ago | parent | prev | next [-]

Mostly wrong place at the wrong time, I guess :(

Plus the chicken and the egg problem. This is mostly from the AerynOS experience : it seems like if you want to write some moderately complicated code then you're becoming the upstream of many libraries. Especially now with Rust's popularity and ecosystem maturity on the rise, it's super hard to convince people (e.g. your boss) that you'd be better of with D compared to e.g. Rust.

WalterBright a day ago | parent [-]

Where D shines is how readable D code is compared to other languages.

nickpp 2 days ago | parent | prev | next [-]

Lack of large “sponsors”.

zem 2 days ago | parent | prev | next [-]

scala is probably the poster child for supporting every paradigm you might want to use :) oz/mozart has more but that was essentially a research/teaching language specifically designed to use a wide range of paradigms in order to demonstrate them.

troupo a day ago | parent | prev [-]

IIRC it was weird and not that great in D1 era, and stalled for a very long time. There were also two competing incompatible runtimes.

Then came the D2 re-write which broke backwards compatibility and took another few years.

In the meantime everyone moved on

destructionator 21 hours ago | parent [-]

> D2 re-write

No such thing happened. D has always been built on the same codebase, and the labels "D1" and "D2" are just arbitrary points on a mostly linear evolution (in fact, the tags D 1.0 and D 2.0 came only 6 months apart; 1.0 was just meant to be a long term support branch, not a different language. It was the addition of `const` that broke most code around release 2.6 but if you update those, old and new compilers generally work.

I'd say where D failed was its insistence on chasing every half-baked trend that someone comments on Hacker News. Seriously, look at this very thread, Walter is replying to thing after thing saying "D has this too!!" nevermind if it actually is valuable irl or not.

DLoupe a day ago | parent | prev | next [-]

For me, D failed to replace C++ because of lack of design. It is more a mix of great features. But once you start learning the details simple things can get very complicated.

For example, function arguments can be "in", "out", "inout", "ref", "scope", "return ref" - and combinations.

Another example is conditional compilation. Great when used sparely, but can otherwise make it very difficult to understand how the code flows.

In the end, reading the source code of the standard library convinced me against it.

(The source code for the C++ standard library is much worse, of course).

senkora 10 hours ago | parent | next [-]

In general, you shouldn’t judge a language by the complexity of its standard library implementation unless you are planning to implement a comparable library.

Application code often only needs a subset of the language and can be much more readable.

The standard library needs to use every trick available to make itself expressive, reusable, backwards-compatible, and easy-to-use for callers. But for your application code you certainly want to make different tradeoffs.

sfpotter 17 hours ago | parent | prev | next [-]

I think it's a great mix of features. I agree it would make sense to separate the wheat from the chaff, but if you're a mature adult, you should be able to decide which features are useful to you and figure a dialect that works.

In general, the design of the standard library is much less alien and baroque than the STL, and is more battries-included, so you spend much less time puzzling over incantations and more time writing code. The code you have at the end is also much more concise and readable.

Likewise, because D is in a lot of ways "C++ with fewer problems and papercuts", I spend way less time figuring out totally inscrutable C++ compilation errors.

Consequently, I can spend more of time writing code and thinking about how to use all D's nice features to better effect. Plus, given how fungible and malleable the language is, it doesn't take a lot of effort to rework things if I want to change them in the future.

Personally, I think this is the main reason D hasn't caught on. It's selling point is that it's pragmatic and doesn't shove a lot of dogma or ideology down your throat. This isn't sexy and there's nothing to latch onto. There are many styles you can write D code in... MANY more than C++: Python-style, C#-style, C++-style, C-style... hell, bash style, MATLAB-style, R style, whatever you want. But for some of these styles, you have to build the tools! The fact that all of this is possible is the result of combining one very practical and ergonomic programming language, with a thousand different QOL improvements and handy tools... plus top tier metaprogramming.

IMO, the major thing holding D back right now is also along the same lines. It offers pragmatism and practicality, but the tooling is still weak. Languages like C++, Rust, and Python totally outclass D when it comes to tooling... but you have to sacrifice flexibility and ergonomics for baroque madness (C++) or BDSM (Rust) or slow and impossible to maintain code (Python). The choice is yours, I guess!

troupo 21 hours ago | parent | prev [-]

> For example, function arguments can be "in", "out", "inout", "ref", "scope", "return ref" - and combinations.

Reminds me of "In case you forgot, Swift has 217 keywords now" https://x.com/jacobtechtavern/status/1841251621004538183

OskarS 2 days ago | parent | prev | next [-]

The "invariants" thing is fantastic, I haven't seen anything like that before and it's great. The C++26 contract stuff is fine, but this seems like a really great way of ensuring type invariants, I think I'd use this way more if it was in C++.

pjmlp a day ago | parent | next [-]

See Design By Contract, and the language that brought its ideas into mainstream, Eiffel.

What D or C++26 can do, is a subset of Eiffel capabilities, or more modern approaches like theorem proving in tools like Ada/SPARK, Dafny, FStar,...

scrubs a day ago | parent [-]

Beat me to it. Eiffel et al have been at the party for a while now.

discardable_dan 2 days ago | parent | prev | next [-]

The issue is most developers do not bother to write any, and the ones that are written are most-often vapid typing failures ("these`int`s cannot be negative" should be handled by a type). I studied this field in grad school, and the entire problem almost always devolves into convincing developers to engage with the system.

wavemode a day ago | parent | next [-]

I find that is the case with almost all methodologies for software quality improvement. If you can't enforce that people follow it then it's not worth anything.

esafak a day ago | parent | prev [-]

It only takes one enlightened CTO :)

destructionator 2 days ago | parent | prev | next [-]

Just a personal anecdote, Walter Bright's Digital Mars C++ compiler also had the contracts (D started life almost literally as recycled code from Mr. Bright's other compilers - he wrote a native Java compiler, a Javascript 1.3 stdlib, and a C++ compiler with a bunch of extensions.... smash those together and you have the early D releases!).

Anyway, I used the DM C++ compiler originally because it was the only one I could download to the high school computers without filling out a form, and pimply-face youth me saw "DESIGN BY CONTRACT" at the top of the website and got kinda excited thinking it was a way to make some easy money coding online.

Imagine my disappointment when I saw it was just in/out/invariant/assert features. (I'm pretty sure D had just come out when I saw that, but I saw `import` instead of `#include` and dismissed it as a weenie language. Came back a couple years later and cursed my younger self for being a fool! lol)

WalterBright 2 days ago | parent | next [-]

The in/out features come into their own when inheritance is in play, i.e. for member functions of classes and interfaces. See https://dlang.org/spec/function.html#in_out_inheritance

`import` is so cool we extended it to be able to import .c files! The D compiler internally translates them to D so they can be used. When this was initially proposed, the reaction was "what's that good for?" It turned out to be incredibly useful and a huge time saver.

The concept is sort of like C++ being a superset of C and so being able to incorporate C code, except unlike C++, the C syntax can be left behind. After all, don't we get tired of:

    struct Tag { ... } Tag;

?
1718627440 a day ago | parent [-]

> struct Tag { ... } Tag;

What's the thing with the syntax? If you don't intend to use the type elsewhere don't give it a tag, if you want, you have to give it a name. (Assuming you are annoyed by the duplicate Tag)

WalterBright 2 days ago | parent | prev [-]

My C++ compiler also implemented contracts back in the 90s: https://www.digitalmars.com/ctg/contract.html

Modern C++ is slowly adopting D features, many of which came from extensions I added to my C++ compiler.

vbezhenar a day ago | parent | prev | next [-]

I feel like this feature could be implemented on top of more universal features.

Checking input parameters is easy, just write asserts at the start of the function.

Checking result requires "destructor" block and some kind of accessible result variable, so you can write asserts in this destructor block which you can place at the start of the function, as well.

Checking class invariants requires a way to specify that some function should be called at the end of every public function. I think, it's called aspect-oriented programming in Java and it's actually useful for more things, than just invariant checking. Declarative transaction management, logging.

There are probably two schools of programming language designs. Some put a lot of features into language and other trying to put a minimal number of features into language which are enough to express other features.

sirwhinesalot a day ago | parent [-]

Having the higher level abstraction built into the language gives extra semantic meaning that can be taken advantage of to build tooling. For example, one could build a symbolic model checker based on the contract specifications. It would be possible to do the same with the lower level features, but a lot harder if they aren't used consistently and correctly.

Same reason function calls are better than arbitrary jumps.

a day ago | parent [-]
[deleted]
peterashford 2 days ago | parent | prev | next [-]

I think they were introduced with Eiffel, which was all about design by contract

johnisgood 2 days ago | parent [-]

Ada has that too, for what it is worth: https://learn.adacore.com/courses/intro-to-ada/chapters/cont... and https://en.wikibooks.org/wiki/Ada_Programming/Contract_Based.... It can be verified at compile time.

But what I love the most is: https://news.ycombinator.com/item?id=43936007

Instead of:

  const MIN_U32 = 0;
  const MAX_U32 = 2 ** 32 - 1;
  
  function u32(v) {
    if (v < MIN_U32 || v > MAX_U32) {
      throw Error(`Value out of range for u32: ${v}`);
    }
  
    return leb128(v);
  }
You can do this, in Ada:

  subtype U32 is Interfaces.Unsigned_64 range 0 .. 2 ** 32 - 1;
or alternatively:

  type U32 is mod 2 ** 32;
and then you can use attributes such as:

  First  : constant U32 := U32'First; -- = 0
  Last   : constant U32 := U32'Last;  -- = 2 ** 32 - 1
  Range_ : constant U32 := U32'Range; -- Range 0 .. 2**32 - 1
Does D have anything like this? Or do any other languages?
12_throw_away 2 days ago | parent | prev | next [-]

Yeah, these look excellent. Am curious if D's invariants can be traced back to Ada/Spark at all (I don't know much about Ada except that it has these sorts of safety features).

johnisgood 2 days ago | parent [-]

Maybe this might help: https://news.ycombinator.com/item?id=44449835

2 days ago | parent | prev | next [-]
[deleted]
fuzztester a day ago | parent | prev | next [-]

>The "invariants" thing is fantastic, I haven't seen anything like that before and it's great.

is it not the same as the one in Eiffel?

almostgotcaught 2 days ago | parent | prev [-]

> Invariants are functions that run at the start and end of every public member function

these are just runtime assertions

EDIT: how am i getting downvoted for copy-pasting literally what the article verifies?

LorenDB a day ago | parent | next [-]

Yes, but they are guaranteed to run at the beginning and end. C/C++ asserts need to handle any return path, whereas D has functionality to mark statements to run at the end of any return path while only being written once.

See also the scope(exit) feature.

almostgotcaught a day ago | parent [-]

You can accomplish the same exact thing with

https://en.cppreference.com/w/cpp/experimental/scope_exit.ht...

WalterBright a day ago | parent [-]

The idea for scope-exit came from Andrei Alexandrescu. See https://dlang.org/articles/exception-safe.html

He demonstrated it with C++ templates, but the D one is far more straightforward.

jayd16 2 days ago | parent | prev | next [-]

I think there's something to be said about them running automatically that is lost when you say they're just asserts.

almostgotcaught a day ago | parent [-]

i don't get it - if do

  int foo(int a) {
    assert(a > 5);
    int b = a * 10;
    assert(b > 50);
    return b;
  }
do you think those asserts don't "run automatically"?
gblargg a day ago | parent [-]

You define the invariants once for the class and they are run around every public function. Done manually you'd probably use a helper object that calls the invariants in its constructor and destructor (have to handle exceptions) that you have to add to every public function's definition.

readthenotes1 2 days ago | parent | prev [-]

Maybe it's the editorial "just"?

Like: software programs can't be that difficult to create properly because they are just 1s and 0s.

johnisgood 2 days ago | parent [-]

This is not the first time someone getting down-voted for using the word "just". I do not know if this really is warranted, however.

Alifatisk 2 days ago | parent | prev | next [-]

I like D, it's fascinating and powerful language. It made it even more curious when I watched Tsodings video on D. One thing that came to my mind when reading the article is that things like int.init instead of 0 and $ as shorthand for array.length does add to the mental load.

One good memory I had is a couple of years ago when I built a little forum using D. Man the site was blazing fast, like the interaction was instant. Good times.

WalterBright 2 days ago | parent | next [-]

The `.init` is there because the default initializer isn't always 0.

fuzztester a day ago | parent | prev [-]

forum.dlang.org is also written in D.

azhenley 2 days ago | parent | prev | next [-]

I really enjoy these lists of interesting features from various languages. They pop up occasionally on HN but now I can’t find them (Hillel Wayne had multiple).

I want a meta list of all these interesting features across languages.

EDIT: I found one! “Micro features I’d like to see in more languages” https://buttondown.com/hillelwayne/archive/microfeatures-id-...

jadbox a day ago | parent | prev | next [-]

Has anyone compared D and Zig? I originally learned D over one weekend and then went on to completed several code competitions- the ergonomics of D are just fantastic.

ImPleadThe5th a day ago | parent [-]

I was wondering the same thing. I have done a few toy projects in Zig, but found some of the docs lacking when you start to get into the weeds.

mamcx 2 days ago | parent | prev | next [-]

I don't use D but think that error handling is one major feature:

https://dlang.org/articles/exception-safe.html

In concrete, looks to me to be the only language that covers the major ways to do it.

(In concrete the `scope` way is the one I found inspiring. . I think the exceptions can go and be replace by it for langs where exceptions are removed)

WalterBright 2 days ago | parent [-]

`scope` is very good at its job. It guarantees that a pointer passed as an argument does not escape the caller's scope. I find it almost as useful as transitive `const`.

burnt-resistor a day ago | parent | prev | next [-]

Invariants, dependent typing, and refinement types FTW.

In Rust land, it really need integration of something like flux into the language or as a gradually-compatible layer.

Can't have safe software without invariant checking, and not just stopping at bounds checking.

zzo38computer a day ago | parent | prev | next [-]

In my opinion, some features of D are good, but I do not like all of them.

CTFE is good.

I do not really like the UFCS; if you want it to be used like a member of the first parameter then you should define it as a member of the first parameter (possibly as a inline function that only calls the freestanding function with the same name, if that is what you want it to do). (You could use a macro to do this automatically if you want to.)

Scoped imports is good, but I think that scoped macros would also be helpful.

Exhaustive switch seem like it might be better if designed differently than it is, but the idea seems to be not bad, in general.

Doxin a day ago | parent | next [-]

Honestly the main reason UFCS is good is when you don't "own" the type your function would need to be a member of. Most common one I run into is the "to" function from the stdlib. You can do this:

    import std.conv;
    
    int foo=3;
    string bar = foo.to!string
But now lets say you want to convert ints to MyCustomInts? You can hardly attach a new "to" member to int, but with UFCS it's easy. Just declare a to function anywhere in scope:

    MyCustomInt to(T)(T v) if(is(T==int)){
        return MyCustomInt.from_int_value(v)
        // or however you actually do the conversion
    }
and it'll magically work:

    int foo=3;
    MyCustomInt bar = foo.to!MyCustomInt;
LorenDB a day ago | parent | prev [-]

UFCS is a bit overreaching but I think it's great for its intended use of chaining expressions on ranges and such.

destructionator a day ago | parent [-]

that actually wasn't its intended use; that's a side effect. The original intended use came from Effective C++ by Scott Meyers: "Prefer non-member non-friend functions to member functions.". It was meant to make that as syntactically appealing as the members.

tealpod a day ago | parent | prev | next [-]

D is one of the most beautiful and very efficient language. I wonder why it never got the attention it deserves.

bachmeier a day ago | parent | prev | next [-]

Interesting that there's nothing on there about C interop (likely reflecting the use cases of the author). D does it all: ImportC (compile C code), BetterC (make a D library part of a C program), and easy C interop in both directions.

p0nce a day ago | parent | prev | next [-]

Strangely my favourite feature after 18 years of D programming (!) is that could place keywords largely in the order you like. It is strangely liberating to be able to put:

    pure nothrow @nogc: 
or

    pure:
    nothrow:
    @nogc: 
or

    pure nothrow @nogc
    {
        block
    } 
Sometimes this helps readability.
quietbritishjim a day ago | parent [-]

Am I missing something? Those three keywords are literally in the same order in all the examples.

p0nce a day ago | parent [-]

Sorry, those keywords work in block like those, or relatively to a single function, in any order.

Joker_vD a day ago | parent | prev | next [-]

> Invariants are functions that run at the start and end of every public member function, and can be used to ensure that the type is always in a valid state.

The ironic thing about this is that it means that the public functions of an object can't, generally, call each other willy-nilly unless they take special precautions: very often, the object's invariants are broken when the execution is in a middle of the object's method. This is not a problem with D, the language merely helps you to uncover this lurking problem which usually simply goes unnoticed until something breaks and someone notices it.

skocznymroczny 18 hours ago | parent | prev | next [-]

I hope some day D gets some WASM story going. Right now the support is very limited, pretty much only through the C subset of D which defeats the point. Supposedly the GC was the main issue, but other GC enabled languages are already thriving in the Web (Go, Java, C#).

There were some efforts to enable D on the web but it seems like these are mostly one man efforts that implement only the minimum features that their specific projects required.

randomNumber7 a day ago | parent | prev | next [-]

When I last looked at D they did a lot of interesting stuff with compile time function execution and s.th. like dependent types that they hacked into the language.

Can someone give me an update about the current state of this?

WhereIsTheTruth a day ago | parent | prev | next [-]

I am an avid fan of D, but they seriously need to massively invest into tooling

The current LSP is _that_ bad, it doesn't even recognize most notable D features such as templates and named arguments..

This should be their #1 priority, as the language is starting to get steam again, they should not miss tat opportunity

I know Walter does not use that kind of tools, but that's becoming a requirement nowadays for young developers

Please, invest into tooling!

skocznymroczny 15 hours ago | parent [-]

Can you actually have proper tooling with templates based language? I think just like C++, how much effort goes into IDEs and still they are subpar. Because with templates, the code doesn't exist until you compile it, so at best you can catch templates instantiations during compile time and then add them to the autocomplete, but anything more advanced than that is not really possible.

WhereIsTheTruth 7 hours ago | parent [-]

Odin have a great LSP, it supports everything the language offers, generics, unions, bitsets, using, same with Zig, their LSP supports most of the notable features even comptime..Both language are quite new, relatively to D

There is no excuse for D, if they can make a great compiler, they surely can make great tooling too

I suggest you give the D LSP a try, I have mentioned templates, but it's very frail for everything else, using `auto`, having a chain of identifiers or even using reference/pointers is enough to confuse it, most of the time

I think it stems from the fact that both Zig/Odin provide a parser/lexer as part of their std, making it easier to just focus on building the tools, the community built parser is not good and lags behind

RcouF1uZ4gsC 19 hours ago | parent | prev | next [-]

The reason that D did not replace C++, IMO is garbage collection.

D was so far ahead of C++98 that it wasn't funny, but garbage collection kept it from being able to be used in the same niche.

D has gotten less dependent on garbage collection but

C++11 (and then 17, 20, 26 (reflection)) have closed the gap enough that it is not the quantum leap that it once was.

sirwhinesalot a day ago | parent | prev | next [-]

All wonderful features. Design by Contract in particular is massively underused in mainstream languages.

kwoff a day ago | parent | prev | next [-]

Dlang always makes me think of two things: Walter Bright, resident of Hacker News. And awesome games I played on Linux in the 2000s: https://en.wikipedia.org/wiki/ABA_Games

throwawaymaths a day ago | parent | prev | next [-]

$ operator. great idea! i believe the only contender is julia (with "end" but that is a SUUPER awkward keyword since end also closes functions)

> Structs and classes can even overload this operator

nope. fuck, now it's a terrible idea

sedatk a day ago | parent | next [-]

> i believe the only contender is julia

C# has "^n" notation that means "length - n". For example:

Take the last element of an array or any other structure that supports indices:

    var a = ar[^1];
Take the last three elements of an array or any other data structure that supports ranges:

    var lastThree = ar[^3..];
bachmeier a day ago | parent | prev | next [-]

I don't see why it would be a terrible idea. It's pretty convenient if you're slicing a multidimensional array.

throwawaymaths a day ago | parent [-]

except are you sure that's what it's doing? it could have been overloaded. you have to ask yourself that every time.

Doxin a day ago | parent | next [-]

Or you can assume it's implemented sanely. Yes if someone overrides $ and does something wildly wrong with it that'll get confusing. That's the fault of whoever wrote that code though, not of $ itself.

You can also overload +, imagine the mayhem if someone did something weird with that. should we ban overloading operators altogether?

throwawaymaths 20 hours ago | parent [-]

> Or you can assume it's implemented sanely

excuse me, have you spent any time in tech? people fucking love to do clever shit that gets people in trouble. using NULL as a not-pointer value signalling absence (without safeguards) is probably the most notorious -- but at least useful, but if you want to get brain meltingly annoying and pervasive, we dont have to stray too far from the idea of operator overloading; c++'s << operator can just go to hell.

1980phipsi a day ago | parent | prev [-]

I've looked at a lot of D code, and I can't recall it ever being misused.

WhereIsTheTruth a day ago | parent | prev [-]

I'll take this:

    Vec2 result = translation + (rotationMatrix * (scalingMatrix * point));

Over this:

    const result = vec2Add(translation, mat2MulVec2(rotationMatrix, mat2MulVec2(scalingMatrix, point)));
Anytime

And that's a very simple example, in Zig, that kind of equations doesn't scale well, when it comes to readability

ioasuncvinvaer a day ago | parent [-]

A problem that I always run into with code using * for multiplication of matrices is which operation they use. Is it element wise or a matrix multiplication?

coldtea a day ago | parent | prev | next [-]

Not the D I expected from reading the title

(come on, a low effort joke now and then is ok, if not for anything else, as an counter-example)

trikko a day ago | parent | prev [-]

The only missing thing is a big sponsor. mozilla -> rust google -> go, kotlin ... n/a -> dlang