Programming Languages on Crack
Walter Bright
newshound2 at digitalmars.com
Thu Jun 17 02:02:58 UTC 2021
I'm sure you can guess where this post is going.
Consider the following controversial language features:
1. implicit variable declaration
2. non-void default initialization
3. macros
4. operator overloading for non-arithmetic purposes
5. arithmetic in version declarations
6. transitive const
7. default constructors
8. direct access to shared values
9. emulation of hardware vector operations
10. recoverable assert failures
11. @trusted statements
Ok, I have successfully sold (1) and (2). But
the others are all uphill struggles for D.
What they all have in common is they're programming
language crack cocaine.
By that I mean that these features make programming faster
and easier. It's like that first hit of crack enables
a superpower where prodigious quantities of code can be
quickly churned out. Wow! What could be wrong with that?
On the other hand, there's dour old Walter dressed in his
severe nun's habit rapping the knuckles with a yardstick anyone
reaching for those treats.
The problem is those features come with a downside. Some
become apparent earlier, some later, even a decade later.
The downside is unreadable, unmaintainable code. Of course,
everyone personally believes that *they* can judiciously
and smartly snort the crack and there won't be downside.
Here's where I play the experience card. All those features
transform code into s**t after a while, no matter who uses them.
Including me. Earlier in my career, I would have happily implemented
all of them, because more power is better. Writing code faster is
better.
No it isn't. Writing code faster isn't better. More power isn't
always better. Having a language that pushes towards writing code
that is more readable is better, safer is better, more maintainable
is better.
Even C, (in)famous for never breaking backwards compatibility,
had implicit function declaratations for 30 years until quietly
taking it behind the woodshed and slitting its throat. (The verbiage
for it just disappeared from the C99 spec.)
That's for context. The rest will be about the @trusted proposal.
The question is: Why is @trusted at the function level rather than
at the statement level? It certainly seems more convenient to apply
it with statement granularity, and it will save 4 characters of typing
over the lambda approach. What could be wrong with that?
And indeed, that so far appears to be the general reaction.
The idea of putting it at the function level is to force (I know,
that sounds bad, but indulge me for a moment) the programmer
to think about the decomposition of programs into safe and unsafe
code. Ideally, untrusted code should be encapsulated and segregated
into separate sections of code, with clean, well-defined, well-considered,
and well thought through interfaces.
At statement level, one just schlepps @trusted in front and gives it
no more consideration. It is thoughtlessly applied, the compiler error
goes away, Mission Accomplished! It might as well be renamed
the @shaddup attribute. Zero thought is given to carefully crafting
a safe interface, because a safe interface to it is not required.
Of course that's tempting.
The ()@trusted{ ... }() is equally bad. Note that there are no parameters
to it, hence NO interface. This was discovered as a way to evade the
intent that @trusted was to be applied to a function interface. It
had never occurred to me to add semantics to disallow it. Even worse,
I myself have been seduced into using this trick.
But I consoled myself that at least it was ugly, and required an extra
4 characters to type. Unfortunately, that hasn't been enough of a
deterrence, as it appears to have been used with abandon.
I recall it was David Nadlinger who originally pointed out that @trusted
at the function level, even if only one statement was unsafe, would
hide safety issues in the rest of the function. Hence the appearance
of the lambda workaround. He is correct. But the real problem was in
failing to encapsulate the unsafe code, and place it behind a sound
and safe interface.
As for my own pre-safe code, I've been gradually upgrading it to be
fully @safe. It's a satisfying result.
Use of @trusted lambdas is a code smell, and making @trusted work at the
statement level is putting the stamp of approval on a stinky practice.
Enabling this is a one way street, it'd be very very hard to undo it.
P.S. A subtler aspect of this is D's semantic reliance on rich function
signatures. This passes critical semantic information to the compiler,
such as inputs, outputs, escapes, live ranges, who's zoomin' who, etc.
Having @trusted at the statement level, with no defined interface to it,
just torpedoes the compiler's ability to reason about the code.
What is the interface to an @trusted statement? Who knows.
An @trusted statement can do anything to the surrounding code in ways nearly
impossible for a compiler to infer. After all, that's why @system code
is called @system in the first place. It doesn't play by the rules.
More information about the Digitalmars-d
mailing list