verona Visibility specifications C++
We've had some internal discussions in the past about visibility and have a rough straw-robot proposal for wider discussion:
In terms of semantics, the proposal was to limit ourselves to public and package, for various reasons:
- In the absence of subclassing,
protected
visibility does not mean anything. - A package will always be compiled together. Anything that is broken by changes to internal implementation details can be refactored at the same time.
- Supporting only two kinds of visibility makes it very easy to decide which to use.
Open questions:
Can interfaces contain package-local methods? If so, what are the restrictions on casting a concrete type to an interface that contains package-local methods (is the cast allowed only in the package that defines the class? Is it allowed only if the interface is, itself, package-local? Are there some more complex rules?). If not, can generic methods be sufficiently expressive within a package for this not to be a problem?
What is the syntax for describing private types / methods? Different case for the start (used by Go and, by convention, C#)? Underscore at the start of package-local (informally used by Python and some C styles)? Explicit package
/ public
keywords (used by most Simula-like languages)?
6 Answer:
I think visibility restrictions have two distinct goals. The first one is limiting the API guarantee, to avoid breaking changes. For this, a “package” visibility is good enough.
The other goal is protecting invariants and allowing local reasoning about code. For this, I would say we want a smaller visibility unit, typically a module (which in most cases is one file).
That gives us three levels, public, package, private.
IIUC, a module is a class, so would private
be visible to other classes inside?
module Mod {
a : U32;
class Foo {
b : F64;
foo() : U32 { return a; } // is this assumed to work?
}
bar() : F64 { return Foo::b; } // or that?
}
What would be the default visibility? I'd assume private
is default, and then we append public
, export
or some other keyword to get them public in the right context.
I would prefer to tag each member individually, rather than use C++'s public
/ private
keywords in the class declaration.
The current approach is that a module is a class, and there isn't a module
keyword, because a module is composed of all of the files in a directory. That is, the translation unit is a directory, not a file.
@rengolin : your example doesn't make any sense to me. You seem to be using instance variables as if they were globals?
I think the "local reasoning" argument is very interesting. Limiting visibility scope does indeed do this, at least to some extent (although not always: a private
member can be exposed via some other public
or package
member, whether that's a method or a nested type). My current inclination is to say that the reasoning scope is also the module, that is, as @davidchisnall points out, it's a single translation unit, and so is "fully visible" to the person/tool doing the reasoning. For me, the argument that this simplifies the choice of visibility level carries a lot of weight.
But the most important issue is the one about interfaces with non-public members. This problem also happens in Pony and TypeScript, and it is serious: if a class with a non-public member fulfills an interface from a different module with non-public members, that other module exposes the non-public API to unexpected code. I think a non-public member can only be fulfilled by a non-public member with the same visibility scope as the interface non-public member.
And that is, in some sense, the best argument for very simple visibility (i.e. just public and package), because there is a simple rule: an interface with a non-public member is only fulfilled by a class/interface from the same package.
For syntax, Pony also uses a leading underscore to indicate private
(Pony uses Smalltalk-ish privacy rules: private methods are package-scope, private fields are class scope - although in Smalltalk, private fields are instance scope). This makes visibility determinable at the call site, which may or may not be a good choice. I'm inclined towards a syntax that encourages non-public as the default, but at the same time I find it more natural when I'm writing code to annotate non-public members.
I propose that, for now, we use two visibility scopes (public and package) and we use the private
keyword for non-public (i.e. package scope) members.
@rengolin : your example doesn't make any sense to me. You seem to be using instance variables as if they were globals?
Yes. I forgot Verona won't have global variables, not even inside "modules", which is the correct abstraction, given that "modules are classes".
It does; however, have static fields of classes (presumably including classes that are modules - at least for interop purposes as the abstraction used to expose C++ globals to Verona), which gives you very similar semantics. Because static fields are not bound to a cown, they can contain only things that you'd be able to store in an immutable object (cown references, immutable pointers, read ends of noticeboards).
An can those static fields be accessed without full lexical context?
module A { // as in, a file in directory A
static f : U32;
class Foo {
foo() : U32 {
// which one is valid? both?
return A::f;
return f;
}
}
If they don't need full lexical context, and modules are classes, then can I also do:
class A {
static f;
class B {
foo() { return f; }
}
}
Which is less meaningful to me. So, I'd favour full lexical context to static field access in all cases.
Read next
- mapbox-gl-js Terrain: expose getElevation API - JavaScript
- !!!!BUG!!!!!! - laravel-world
- amplify init gives AccessDenied error - When trying to clone a repo from another creator - amplify-cli
- Support winit 0.25 - imgui-wgpu-rs
- AppIntro Did you forget about old good Java? - Kotlin
- frigate multiple events for a single moving object Python
- uninitialized constant Motor::ApiBaseController - Ruby motor-admin
- Favicon goes missing when tab is playing audio - CSS Firefox-UI-Fix