Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

Visibility

The visibility of a feature defines where this feature can be accessed.

Scopes

A block consists of indented statements or statements enclosed by braces { }.

A scope is defined by a block. Scopes can be nested. For example the declaration of an inner feature is a statement in a surrounding block or the block in an if-statement is nested in the block that contains the if statement.

Unqualified Visibility

Routines

The unqualified visibility of a routine determines where this routine can be called without a qualifying target t as in t.f. A routine f declared in scope s is visible for an unqualified call in s itself, all nested scopes of s and all inner routines declared in the same scope s or in any nested scope within s:

Fields

Compared to routines, fields are not visible for unqualified calls before their declaration:

Assignment Visibility

Unqualified mutable fields can be the target of an assignment whenever the field is visible for an unqualified call.

Masking

Masking can make a field inaccessible by replacing it with a field with an equal name defined in the same or inner scope.

Masking in Inner Scopes

Features in an inner scope can use the same name as those in an outer scope, effectively masking the outer features:

Outer fields can nevertheless be accessed via a qualified access as follows

TBD: Masking a field in an outer scope can cause confusion and severe programming errors. It might make sense to raise an error if masking occurs.

Masking Fields by Declarations

For convenience, the field declaration operation allows to mask fields declared previously in the same scope:

This example declares different fields with the name x and different ranges of visibility. Note that a field declaration does not mask the field during the evaluation of the expression of the newly declared field's initial value. I.e., in the second declaration above x := x as f64, the x on the right hand side of the assignment refers to the x declared in the previous line.

To reduce confusion masking fields will need some restrictions:

  • A field declaration may not mask a routine declared in the same scope
  • A field declaration may not mask a field declared in the same scope if the masks or the newly declared field was made visible to another source file or another module
  • Qualified accesses: To avoid ambiguity, qualified accesses to fields that mask or are masked by other fields of the same scope should not be allowed.

Masked fields of the same scope may outlive their scope at runtime, as in this example:

This will produce the same output as the previous example, but the four different fields with name x will be kept during runtime.

TBD: Masking a field in the same scope might be less confusing and error-prone than masking a field in an outer scope. It should therefore be allowed.

Features declared in inner Blocks

Features declared in inner blocks have visibility restrictions similar to those declared in inner features:

The visibility in loops is a bit more complicated, as shown in this example:

In a loop, within the for-block, index variables that are not iterators can be accessed after their declaration and in the next-part of their declaration (after the comma). Index variables are accessible in the while- and until-blocks. Features declared in the while-block are also accessible in the until-block. The else block can access all index variables before the first iterator index variable (one that uses in in the for-block)

The reason for this complex visibility is that it is guaranteed that a for-block is executed fully before the while block is entered. Additionally, the until-block can only be entered after the while block was fully executed.

On the other hand, the else block may be entered as soon as an iterator index variable reaches the end of the iterated set, so only previously declared index variables are guaranteed to be set.

Qualified Visibility

The qualified visibility determines if a feature f is visible via a qualified access target.f.

Qualified Call Visibility

TBD:

  • features within one source code file should probably be fully visible for qualified accesses within the same file
  • features in another source code file should be made visible, e.g., by a keyword public, to be visible for qualified calls
  • features could be made visible to specific other friend features via visible to friend or, if all subclasses of friend should be included, via visible to * : friend or a similar syntax.

Qualified Assignment Visibility

Qualified assignments are not allowed:

Even qualified assignments to masked outer fields are not allowed:

Module Visibility

A feature declared in another module is visible to other modules only if it is exported from the modules. TBD: Need to decide how to do this, mark it export or similar.

An exported feature f declared in scope s of a module m is visible for an unqualified call in another module m2 if that call happens in a scope s2 that is an inner scope of s and it was not masked.

Unqualified Visibility

Example: In module A:

export a
{
  export x { }
  export b
  {
    export y { }
    public z { }
  }
  public c
  {
    export y { }  // *** illegal, compiler error: cannot export y since outer c is not exported
    public z { }
  }
}

In module B:

a.b.new_feature_in_b
{
  // ... x and y are visible here, c and z are not ...
}

a.c.new_feature_in_b // *** illegal, compiler error: c not exported
{
  // ...
}

Qualified Visibility

Qualified accesses to features of another module are restricted to the features that are exported from that module.

Module Assignment Visibility

No fields declared in another module are visible as the target of an assignment in another module.

Visibility for Inheritance

Some languages treat visibility in heirs of a class differently. Java for example knows protected members and has just introduced sealed classes JEP 360.

For Fuzion, the question is whether it would be useful to make a feature visible as a type without allowing calls to the feature, in particular without allowing calls in an inheritance clause.

TBW: Need to decide if this is important enough and if so, do we need specific language support for this or can this be modelled differently (e.g., by adding a unit type argument to a feature that is made visible only to the permitted heirs)?

Visibility of Types vs. Constructors

In some situations, a type that is defined by a constructor feature should be accessible, but not the constructor itself, e.g.,

Here, we might want to make the type dice.face visible so that one can declare

sum(toss1, toss2 dice.face) => toss1.n + toss2.n

while it should be disallowed to create a new face as in

cheat := dice.face 7

Visibility Modifiers in other languages

Java

private for local visibility, protected for local plus sub-class visibility,   for package visibility, public for unrestricted visibility.

Perl

Perls uses our, my and local to extend the scope of variables in somewhat confusing ways. our and my are, however, short and might be useful as simple markers.

Oberon

Oberon replaces the def-files in Modula-2 by a marker * for everything that is exported.

C

C uses .h files to define what is accessible and static to make a declaration inaccessible by other source files.

Eiffel

Eiffel uses a clients clause in a feature declaration. The clients clause explicitly lists the classes to which a feature is to be visible, while classes ANY and NONE handle the special cases of all classes or private features.

Visibility modifiers in Fuzion

The idea is to have three modifiers priv, module and pub to modify the visibility for calls and when used as type. Since priv is also the default visibility, the priv modifier can be omitted to have private (file local only) visibility.

Visibility
Modifier
Current file Current module Other module
priv or   ✅ Yes❌ No❌ No
module ✅ Yes✅ Yes❌ No
pub ✅ Yes✅ Yes✅ Yes

When a different visibility for a type declared by a constructor is desired, a variant of type:priv, type:module and type:pub can be added to modify the type visibility of constructors.

Visibility
Modifier
For calls As types
Current file Current module Other module Current file Current module Other module
priv, priv type:priv, type:priv or   ✅ Yes❌ No❌ No ✅ Yes❌ No❌ No
priv type:module or type:module ✅ Yes❌ No❌ No ✅ Yes✅ Yes❌ No
priv type:pub or type:pub ✅ Yes❌ No❌ No ✅ Yes✅ Yes✅ Yes
module type:priv ✅ Yes✅ Yes❌ No ✅ Yes❌ No❌ No
module or module type:module ✅ Yes✅ Yes❌ No ✅ Yes✅ Yes❌ No
module type:pub ✅ Yes✅ Yes❌ No ✅ Yes✅ Yes✅ Yes
pub type:priv ✅ Yes✅ Yes✅ Yes ✅ Yes❌ No❌ No
pub type:module ✅ Yes✅ Yes✅ Yes ✅ Yes✅ Yes❌ No
pub or pub type:pub ✅ Yes✅ Yes✅ Yes ✅ Yes✅ Yes✅ Yes

Visibility and feature inheritance

In case complex is exported as a public part of a module, we might not want to expose the fact that it inherits from trigo for implementation purposes, so we can inherit in private: