these are just runtime assertions
EDIT: how am i getting downvoted for copy-pasting literally what the article verifies?
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"?Like: software programs can't be that difficult to create properly because they are just 1s and 0s.
See also the scope(exit) feature.
https://en.cppreference.com/w/cpp/experimental/scope_exit.ht...
He demonstrated it with C++ templates, but the D one is far more straightforward.
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)
`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;
?
Modern C++ is slowly adopting D features, many of which came from extensions I added to my C++ compiler.
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?is it not the same as the one in Eiffel?
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.
Same reason function calls are better than arbitrary jumps.
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,...
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.
That is basically table stakes for a new language now.
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.
`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.
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.
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.
* https://learn.microsoft.com/en-gb/dotnet/api/system.linq.enu...
* https://learn.microsoft.com/en-gb/dotnet/api/system.linq.enu...
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.
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).
> Just don't allocate with the GC
"virality" is not just a word you can ignore.
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.
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.
(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.)
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.
Then came the D2 re-write which broke backwards compatibility and took another few years.
In the meantime everyone moved on
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.
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)
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.
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.
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.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.
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.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-...
> Structs and classes can even overload this operator
nope. fuck, now it's a terrible idea
You can also overload +, imagine the mayhem if someone did something weird with that. should we ban overloading operators altogether?
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..];
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.
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 = foo.to!MyCustomInt;
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.
Alifatisk•11h ago
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•8h ago