Thoughts on Backward Compatibility

Carl Sturtivant sturtivant at gmail.com
Sun Feb 18 16:47:36 UTC 2024


On Friday, 16 February 2024 at 04:38:03 UTC, H. S. Teoh wrote:
> On Fri, Feb 16, 2024 at 01:44:51AM +0000, Paul Backus via 
> Digitalmars-d wrote: [...]
>> ### What can we learn from this for D?
>> 
>> First, that the success and popularity of a programming 
>> language is mostly determined by factors other than stability 
>> and backward compatibility (or lack thereof).
>
> +100.  Over the past 5-10 years or so, I've been finding myself 
> wishing that D would introduce some breaking changes so that it 
> could clean up some of its dark, ugly corners that have been 
> like flies in the ointment for a long time.
>
> At the same time, I'd had deprecations and breaking changes 
> that are really rather minor, but extremely frustrating, 
> because:
>
>
>> Second, that even without an edition bump, small-scale 
>> breaking changes with easy migration paths aren't a big deal.
>
> The worst feeling is when you upgrade your compiler, and 
> suddenly you find yourself having to do major code surgery in 
> order to make previously-fine code work again.  Having an easy 
> migration path for breaking changes is very important.
>
> I'd also add that the migration path should be *easy*: it 
> shouldn't take too much thought to upgrade the code, and should 
> not involve tricky decisions based on subtle semantic 
> differences that require deep understanding of the code to make 
> the right choice.
>
> The std.math.approxEqual deprecation is a major example that I 
> keep running into.  It's intended to be replaced by isClose, 
> with all the right intentions. But it was frustrating because 
> (1) it didn't feel necessary -- previous code worked fine even 
> if there were some pathological cases that weren't being 
> handled correctly.  (2) The deprecation message didn't give a 
> clear migration path -- isClose has different parameters with 
> subtly different semantics from approxEqual, and it wasn't 
> obvious how you should replace calls to approxEqual with 
> equivalent calls to isClose.  There were also no easy defaults 
> that you could use that replicated the previous behaviour; you 
> had to sit down and think about each call, then look up the 
> docs to be sure.  (3) The choice of name felt like a serious 
> blunder, even if it was made for all the right reasons.
>
> All of these added up to a very frustrating experience, even if 
> the intentions were right.
>
> If we could have done this over again, I'd have proposed to 
> keep the old semantics of approxEqual, perhaps add another 
> parameter that would use the new semantics. And make sure the 
> deprecation message is clear about how exactly you go about 
> deciding what to put in the new parameter. I.e., for lazy 
> authors not changing anything would let their code continue to 
> work as before; if they wanted to have the new semantics they'd 
> have to explicitly opt in.
>
>
>> Third, that even with an edition bump, large-scale breaking 
>> changes that make migration difficult should probably be 
>> avoided.
>
> Yes, large breaking changes are a no-no.  Unless my old code 
> can continue compiling as before, and I have to opt-in to the 
> new stuff. Editions would help with this, but it still depends 
> on the execution. There should always be a good migration path 
> that doesn't require you to rewrite 5-10 year old code that you 
> no longer remember the details of and can no longer confidently 
> reimplement without spending disproportionate amounts of time 
> to re-learn the ins and outs of it.
>
>
>> Fourth, that breaking changes should be used to give D 
>> programmers more of what they already like about D, not to 
>> take the D language in new directions.
>
> TBH, @nogc, dip1000, @live, etc., feel a lot like D trying to 
> go in entirely new directions.  The fact that it's been years 
> and still practically nobody understands exactly how it works 
> and what it does, is not a good sign. And all this while things 
> like `share` and static initialization of AA's are stagnating.  
> Built-in AA's are one of my major reasons for choosing D, and 
> seeing it languish for years with elementary features like 
> static initialization not fixed is quite disheartening.  Worse 
> when it feels like D wants to move to newer pastures when its 
> current features are still half-done and has problematic corner 
> cases.  I.e., what I like about D is stagnating, while new 
> features that I have little interest in are being pushed on me.
>
>
>> To Walter, Atila, and the rest of D's leadership, I hope this 
>> post provides some helpful data points for you to take into 
>> account when designing D's language editions and planning 
>> future language changes.
>> 
>> To everyone else reading this, I'd like to leave you with one 
>> last question: what do **you** like about D? What strengths 
>> does D have, as a language, that you'd like to see become even 
>> stronger?
> [...]
>
> What I like about D:
>
> - Meta-programming power.
>    - CTFE should be improved.  By a lot.  It was a big 
> disappointment
>      that Stefan's newCTFE never materialized.  IMO we should be
>      improving this story instead of trying to chase rainbows 
> like ARC
>      with @live and dip1000 and what-not.  We should make this 
> so good
>      that I'll never need to use an external codegen utility 
> again. And
>      it should not introduce crazy compile times. This is a 
> primary D
>      strength, its story should be maximally optimized.
>
>    - The template story should be improved.  There should be a 
> way of
>      working with templates that cut down on needless bloat.  
> Lots of
>      room for exploration here.  We shouldn't be confined by C++
>      limitations here. This is one of D's primary strengths and 
> where we
>      can pioneer even more.  One area is improving IFTI to make 
> it work
>      for even more common cases.  Another is recognizing common 
> patterns
>      like chains of ranges, and optimizing symbol generation so 
> that you
>      don't end up with unreasonably huge symbols. Esp. when 
> it's a
>      one-of-a-kind UFCS chain (it's unlikely you're ever going 
> to have
>      exactly the same chain twice with exactly the same template
>      arguments -- no point encoding every argument in the 
> symbol, just
>      an ID that gets incremented per instantiation is good 
> enough).
>
>    - Compile-time introspection and DbI. This is another huge D
>      strength, and we should be working on streamlining it even 
> more.
>       - Clean up __traits(), make std.traits more sensible.
>       - Fix things like scoping issues with static foreach.  
> Introduce
>         local aliases so that static foreach doesn't need crazy 
> hacks
>         with {{...}} and temporary templates just for injecting 
> new
>         identifiers per iteration without running into multiple
>         declaration errors.
>       - Improve the syntax for retrieving members of some 
> symbol.
>         Something prettier than __traits(getAllMembers,...). 
> This is a
>         primary D strength, it should be dressed in better 
> syntax than
>         this.
>       - Maybe first-class types to make the metaprogramming 
> story even
>         more powerful.
>
> - GC.  Instead of bending over backwards trying to woo the 
> @nogc crowd
>   who are mass migrating to Rust anyway, what about introducing 
> write
>   barriers that would allow us existing D users to use a much 
> more
>   competitive GC algorithm?  Stop-the-world GC in 2024 
> shouldn't even be
>   a thing anymore. We aren't in 1998 anymore.  D should embrace 
> the GC,
>   not sacrifice it for the sake of wooing a crowd that isn't 
> likely to
>   adopt D regardless.  Instead of trying to get away from the 
> GC, what
>   about making the GC experience better for existing D users?
>
> - Built-in AA's.  It's been at least a decade.  Why is static
>   initialization support still sketchy?
>
> - Built-in unittests. The default experience should be 
> top-of-the-line.
>   We shouldn't need to import a dub package for something 
> beyond the
>   current dumb built-in test runner. Named unittests, the 
> ability to
>   select which tests to run, the ability to run all tests 
> regardless of
>   failure and show stats afterwards -- these are all basic
>   functionalities that ought to work out-of-the-box.
>
> - Automatic type & attribute inference. `auto` was 
> revolutionary when I
>   first joined D (C++ got it only years later).  We should 
> improve type
>   and attribute inference to the max (e.g., in default 
> parameters for
>   enums: there should be no need to repeat the enum name). 
> Nobody like
>   spelling out attribute soup, just like nobody likes spelling 
> out
>   explicit types when it's already obvious from context. The 
> compiler
>   should automate this to the max.  A little bit of breakage 
> here IMO is
>   acceptable as long as it gets us to an even better place.  
> Have
>   negated attributes be a thing as well.  In fact, make 
> attributes
>   first-class citizens so that we can use DbI / metaprogramming 
> to
>   manipulate it.  This is what we should be focusing our 
> efforts on
>   instead of trying to woo an amorphous group of hypothetical 
> potential
>   users somewhere out there who aren't particularly likely to 
> adopt D to
>   begin with.

+100
And the subtle business mentioned first of cleaning up dark ugly 
corners with breaking changes gets +110. Here are a couple of 
bottom level ones that jumped out at me in the last few days.

--- Fix void to be a type like any other and not an unconsidered 
edge case to trip over.

--- Remove automatic conversions between signed and unsigned so 
that unconsidered bizarre semantics is excluded from D.

Here's a sane take on GC, not more ideology.
https://bitbashing.io/gc-for-systems-programmers.html
--- Make the GC a 21st century GC! And acknowledge that using it 
by default makes sense as per the short article.






More information about the Digitalmars-d mailing list