Mutable struct vs closure

I agree that encapsulation vs flexibility is an important discussion. Yes, you can get around encapsulation in many other languages even when they were designed for it. In Java for example you can access and modify private fields using reflection. I think this argument misses the point a bit – IMO the point isn’t that it’s possible, but that it’s so easy to do it in Julia, and that it seems tolerated, or even encouraged. I doubt you’ll find anyone seriously suggesting a workaround based on reflection to access private members on a Java forum, and any such code would likely not pass code review. But I’ve lost count of how many posts I’ve seen on this forum suggesting this, or packages doing it. Sometimes accompanied with warnings, but I’m not sure how much that helps.

A recent example is this thread where a new user asked for help on indexing, and was provided one “proper” solution, and one shorter solution using internal methods. No-one opposed. As we can see in a later post, the approach using internal methods was chosen as a solution.

Some more examples here, here, here, here, here, here, here.

As for risks/safety, it’s not about malicious code IMO, but very well-intentioned code, that becomes unmanageable over time as you reach a large code-base with many authors. Joshua Bloch (author of Effective Java, and many core Java features) writes the following:

"Minimize the accessibility of classes and members"

The single most important factor that distinguishes a well-designed component from a poorly designed one is the degree to which the component hides its internal data and other implementation details from other components. A well-designed component hides all its implementation details, cleanly separating its API from its implementation. Components then communicate only through their APIs and are oblivious to each others’ inner workings. This concept, known as information hiding or encapsulation, is a fundamental tenet of software design.

Information hiding is important for many reasons, most of which stem from the fact that it decouples the components that comprise a system, allowing them to be developed, tested, optimized, used, understood, and modified in isolation. This speeds up system development because components can be developed in parallel. It eases the burden of maintenance because components can be understood more quickly and debugged or replaced with little fear of harming other components. While information hiding does not, in and of itself, cause good performance, it enables effective performance tuning: once a system is complete and profiling has determined which components are causing performance problems, those components can be optimized without affecting the correctness of others. Information hiding increases software reuse because components that aren’t tightly coupled often prove useful in other contexts besides the ones for which they were developed. Finally, information hiding decreases the risk in building large systems because individual components may prove successful even if the system does not.