2 days ago
On Thu, Oct 30, 2025 at 05:53:15PM +0000, Steven Schveighoffer via Digitalmars-d wrote:
> On Thursday, 30 October 2025 at 16:02:36 UTC, jmh530 wrote:
> 
> > Steve’s point was a little confusing. He said it’s a unit test
> > problem and not an accessibility problem. Regular unit tests should
> > be able to access the rest of the module but documented unit tests
> > shouldn’t. But would we really want to strictly enforce that for
> > documented unit tests?  The point might be that it’s not an issue of
> > other parts of the module accessing the internals of the unittest,
> > it’s about the ability of the unittest to access the private
> > elements of the module. So I don’t think the equivalent of
> > scope-private would solve that issue. You would need to make
> > everything else in the module scope-private instead of private,
> > which you may not want to do. Seems like it would need something
> > else.  Maybe instead of a “friend” you would need an “enemy”. ;)

lol, to further distance ourselves from C++, we should introduce the 'enemy' keyword in lieu of 'friend'. :-P


> If you look at it another way, you can't document usage with a unittest that is not inside the module. There simply isn't a way to tie an external unittest to the function, it has to go right after it.

Yes.

Though we already have a related (but tangential) issue with this inside templated aggregates: if the template never gets instantiated, none of the unittest blocks inside it would ever instantiate.  And the unittests would also instantiate multiple times, once per instantiation, which leads to a lot of bloat when the tests don't actually depend on the template arguments of the current instantiation (i.e., they manually specify specific instantiations that they are testing).


> This is a unittest problem, but in fact, it's very nice to have the documented unittest required to be right after it (for maintenance purposes). The better solution is to prevent documented unittests (or at least provide the *ability* to prevent) from accessing private data.
[...]

Do I smell a further abu^Wextension of the 'static' keyword to prevent unittests from accessing private members? ;-)

```d
	/// Your average typical ddoc'd aggregate
	struct Stuff {
		private int x;

		/// Your average typical member function
		void member() {}

		/// My awesome ddoc'd unittest!
		static unittest {
			Stuff s;
			s.x; // error: cannot access private member x
		}
	}
```


T

-- 
Synonym rolls: just like grammar used to make them.
2 days ago

On Thursday, 30 October 2025 at 17:53:15 UTC, Steven Schveighoffer wrote:

>

If you look at it another way, you can't document usage with a unittest that is not inside the module. There simply isn't a way to tie an external unittest to the function, it has to go right after it.

This is a unittest problem, but in fact, it's very nice to have the documented unittest required to be right after it (for maintenance purposes). The better solution is to prevent documented unittests (or at least provide the ability to prevent) from accessing private data.

I'd say it's not wrong, private members should be documented. The problem looks more like ddoc doesn't differentiate between public and private documentation.

2 days ago
On Thursday, 30 October 2025 at 13:20:34 UTC, Kapendev wrote:
> On Thursday, 30 October 2025 at 06:21:41 UTC, Peter C wrote:
>> And for those that argue.. well .. in Ada.. packages are the unit of encapsulation too.. so what's the big deal with D having choosing to have the module as the unit of encapsulation?
>>
>> I'd answer: it is very likely, that if Ada were designed today, in a world dominated by class-oriented programming, the designers would likely blend the two models:
>>
>>  - Keep packages as the unit of modularity.
>>  - Give types the ability to maintain their own encapsulation boundary (so invariants are truly type‑owned) - ( which is what a 'scopeprivate' like attribute .. would do).
>>
>> Then D would align with the modern 'a class maintains its own invariants' model.
>
> There is nothing modern about this. It's just a *preference* thing.
> Also, did you ask the Ada devs whether they would even want that? Sure, it's an old language, but that doesn't mean much.

That's where I strongly disagree.

The lessons from the Swift community is there for everyone to see - and learn from.

It wasn't just a matter of preference. Public by default or private by default, that is a matter of preference. Removing the concept of an encapsulated type... that is in a whole different ballpark.

It was the Swift programmers, not the language designers, that eventually got Swift corrected (from the perspective of the programmers) - in line with what (a majority of) programmers expected, and not what the designers wanted.

It's not that scopeprivate is a bad idea. It's that D doesn't have a majority of programmers who think its a bad idea .. and likely never will.

But if it does, then I'll be the first to say .."I told you so" ;-)

2 days ago
On Thursday, 30 October 2025 at 21:30:16 UTC, Peter C wrote:
> 
> ..
> It's not that scopeprivate is a bad idea. It's that D doesn't have a majority of programmers who think its a bad idea .. and likely never will.
>

oops. I flipped that the wrong way ;-)



2 days ago
On Thursday, 30 October 2025 at 13:31:48 UTC, Zealot wrote:
> On Thursday, 30 October 2025 at 06:21:41 UTC, Peter C wrote:
>> But I am genuinely open to listening to a good, rational argument, make no mistake about that.
>
>
> you ignore the amount of code adding scopeprivate would affect. for example any code that uses __traits getVisibility would be likely broken by introducing a new case.
>
> metaprogramming also would have new weird special cases.
>
> funnily tupleof would keep working (because it's arguably broken now); protection doesn't work the way you think it does anyway, consider this:
> ```d
>
> --- foo.d
> module foo;
> import std;
>
>     class A {
>         private int a = 42;
>         protected  int b = 43;
>         int c = 44;
>     }
>
>     --- bar.d
>     import std;
>     import foo;
>     void main() {
>         auto a = new A;
>         static foreach(i; 0..a.tupleof.length) {
>         	writeln(__traits(getVisibility, a.tupleof[i]), " => ", a.tupleof[i]);
>         }
>     }
>
>     ```

Thanks for raising the discussion ;-)

I think I might be missing your point though (i.e. the code below works just fine):

// ===============

module foo;
@safe:
private:

import std;

public class A
{
    scopeprivate int p = 1;
    private int a = 42;
    protected  int b = 43;
    int c = 44;
}

// ===============

module bar;
@safe:
private:

import std;
import foo;

void main()
{
    auto a = new A;

    static foreach(i; 0..a.tupleof.length)
    {
        writeln(__traits(getVisibility, a.tupleof[i]), " => ", a.tupleof[i]);
    }
}

// ===============

scopeprivate => 1
private => 42
protected => 43
public => 44

2 days ago
On Wednesday, 29 October 2025 at 19:59:15 UTC, jmh530 wrote:
>
> ...
> The complexity I would be more worried about is the impact on the compiler code base. I don't have the ability to speak to that, but I take people like Dennis and Walter at their word that it's something to be concerned about. But if the argument against it is that complexity will increase, then the argument carries more weight if we can spell out more concretely how it will increase.

Here there may well be an argument to make. Although it took me less than an hour
to implement scopeprivate, I cannot really judge its impact on the rest of the compiler code. So I agree, that could be a concern.

But even then, is that a problem with the idea of scopeprivate, or the complexity in the compilers code, or the enourmous amount of features it already has to implement.

I'd say the 'idea itself' - of scopeprivate, is the less concerning issue here.

>
> This complexity vs. expressiveness trade-off shows up in a number of places in D ...

It's also about local reasoning, which is hard to do when the encapsulation barrier of the module supercedes the encapsulation barrier of your type.

Swift made the same mistake - and had to correct it. Good on them for having the courage to go all the way, instead of just implementing scopeprivate (i.e. reverting private to what the mental model that programmers held, and introducing fileprivate.

Here's a nice talk, although a little drawn-out, about the importance of being able to reason locally.

https://youtu.be/bhizxAXQlWc

2 days ago
On Thursday, 30 October 2025 at 21:49:45 UTC, Peter C wrote:
> On Thursday, 30 October 2025 at 13:31:48 UTC, Zealot wrote:
>>     [...]
>
> Thanks for raising the discussion ;-)
>
> I think I might be missing your point though (i.e. the code below works just fine):
>
> // ===============
>
> module foo;
> @safe:
> private:
>
> import std;
>
> public class A
> {
>     scopeprivate int p = 1;
>     private int a = 42;
>     protected  int b = 43;
>     int c = 44;
> }
>
> // ===============
>
> module bar;
> @safe:
> private:
>
> import std;
> import foo;
>
> void main()
> {
>     auto a = new A;
>
>     static foreach(i; 0..a.tupleof.length)
>     {
>         writeln(__traits(getVisibility, a.tupleof[i]), " => ", a.tupleof[i]);
>     }
> }
>
> // ===============
>
> scopeprivate => 1
> private => 42
> protected => 43
> public => 44

a) tupleof ignores the privacy attributes. tupleof allows you to access private state.
b) any code that exists and uses getVisibility now has a new case introduced which it doesn't handle, so you would break all that code by introducing scopeprivate.
1 day ago
On 10/29/2025 8:48 PM, Steven Schveighoffer wrote:
> There has always been only ONE deficiency for this, and that is documented unittests being allowed to use private symbols in the module. But this is a unittest problem, and not a private problem.
> 
> Note that the OPs argument against (non-documented) unittests being able to access private data is ironically something I view as a necessity -- I often test functions on a type and then examine the private details to make sure the state is as I expect it.

Since unittests aren't part of the finished program, preventing it from having access is not terribly useful. It's like you can prefix statements with `debug` when needing to override pure / @safe attributes.

1 day ago

On Thursday, 30 October 2025 at 07:09:22 UTC, Sergey wrote:

>

On Thursday, 30 October 2025 at 03:48:01 UTC, Steven Schveighoffer wrote:

>

I think it's unfair to judge all new questions with this lens, it's showing a bias that is not really focused on the topic at hand.

Thank you. I agree

I didn’t mean newcomers can’t make valuable proposals and ideas. And newcomers of course more than welcome :)

Just the whole current of discussion from topic starter shows the same style as original claptrap had.

I have never argued for class private, search the forum and find a message where I have.

I think you missed the sarcasm in my earlier post in this thread. IE, he says he doesnt care about D and yet posts over and over about this one topic. Hence my sarcastic "yes you're absolutely right, are you happy now?"

If it's true he doesnt care about D, as he claims, the only other interpretation of his behaviour is that he's desperate for someone to tell him that he is right. He's the archetypal xkcd "Someone is WRONG on the internet".

1 day ago

On Friday, 31 October 2025 at 00:50:54 UTC, Walter Bright wrote:

>

On 10/29/2025 8:48 PM, Steven Schveighoffer wrote:

>

There has always been only ONE deficiency for this, and that is documented unittests being allowed to use private symbols in the module. But this is a unittest problem, and not a private problem.

Note that the OPs argument against (non-documented) unittests being able to access private data is ironically something I view as a necessity -- I often test functions on a type and then examine the private details to make sure the state is as I expect it.

Since unittests aren't part of the finished program, preventing it from having access is not terribly useful. It's like you can prefix statements with debug when needing to override pure / @safe attributes.

I've already started replying again, even though I said I wouldn't, but this is an important point to get right, Walter. I agree normal unittests should be able to access private members, I said as much in the second paragraph above.

What we are talking about is documented unittests.

Consider the following documented unittest:

private int foo(int x) => 2 * x;

/// Do some interesting stuff
public int doStuff() => 42;

/// How to use doStuff
unittest {
   auto val = doStuff();
   assert(foo(val) == 84);
}

Now, what happens here is that the ddoc system will show the documented unittest for doStuff in the documentation for it.

But it has used a private function! The user has to go look at the source code of the module, to understand what foo actually does, to undestand what doStuff actually does.

The point of a documented unittest is to show to a user how a function works. If it is using private undocumented symbols, then the user is clueless as to what it means.

So my recommendation would be to either ban private symbol use in documented unittests, or to provide a mechanism to allow unittests to be banned from using private symbols (maybe public unittest? I don't know)

Note that we had problems with code on dlang.org for years on this front, and it was finally solved by writing a tool that parses d code, and outputs all the unittests as external functions to ensure they aren't using private symbols.

See the code here: https://github.com/dlang/tools/blob/master/tests_extractor.d

It would be very nice if we didn't have to do this extra step.

-Steve