erg/doc/EN/syntax/19_visibility.md
2022-08-11 10:53:40 +09:00

4.1 KiB

Visibility

Erg variables have the concept of visibility. All variables we have seen so far are called private variables. These are variables that are invisible to the outside world. For example, a private variable defined in the foo module cannot be referenced from another module.

# foo.er
x = "this is an invisible variable"
# bar.er
foo = import "foo"
foo.x # AttributeError: Module 'foo' has no attribute 'x' ('x' is private)

In contrast, there is also a public variable, which can be referenced externally. Public variables are defined with ..

# foo.er
.x = "this is a visible variable"
# bar.er
foo = import "foo"
assert foo.x == "this is a visible variable"

Private variables do not need to be marked with anything, but can be marked with :: or self:: (or Self:: for types, etc.) to make them explicitly private. A module can also be module::.

::x = "this is an invisible variable"
assert ::x == x
assert self::x == ::x
assert module::x == ::x

In the context of mere sequential execution, private variables are almost synonymous with local variables. They can be referenced from inner scope.

::x = "this is a private variable"
y =
    x + 1 # exactly module::x

The :: allows you to distinguish between variables with the same name in a scope. Specify the scope of the variable you want to reference on the left. For the top level, specify module. If not specified, the innermost variable is referenced as in the normal case.

::x = 0
assert x == 0
y =
    ::x = 1
    assert x == 1
    z =
        ::x = 2
        assert ::x == 2
        assert z::x == 2
        assert y::x == 1
        assert module::x == 0

In the scope of an anonymous subroutine, self specifies its own scope.

x = 0
f = x ->
    log module::x, self::x
f 1 # 0 1

:: is also responsible for accessing private instance attributes.

x = 0
C = Class {x = Int}
C.
    # Top-level x is referenced (warns to make it module::x)
    f1 self = x
    # x of instance attribute is referenced
    f2 self = self::x

Visibility in external modules

A class defined in one module can actually define methods from an external module as well.

## foo.er
.Foo = Class()
# bar.er
{Foo; ...} = import "foo"

Foo::
    private self = pass
Foo.
    public self = self::private()

.f() =
    foo = Foo.new()
    foo.public()
    foo::private() # AttributeError

However, both of those methods can only be used within that module. Externally defined private methods can be referenced by methods of the Foo class only within the defining module. Public methods are exposed outside the class, but not to outside the module.

# baz.er.
{Foo; ...} = import "foo"

foo = Foo.new()
foo.public() # AttributeError: 'Foo' has no attribute 'public' ('public' is defined in module 'bar')

Also, you cannot define a method on the type you are re-exporting. This is to avoid confusion when a method is found or not found depending on the module from which it is imported.

# bar.er
{.Foo; ...} = import "foo"

.Foo::
    private self = pass # Error
.Foo.
    Foo:: public self = self::private() # Error

If you want to do something like this, define a patch.

# bar.er.
{Foo; ...} = import "foo"

FooImpl = Patch Foo
FooImpl::
    private self = pass
FooImpl.
    public self = self::private()
# baz.er
{Foo; ...} = import "foo"
{FooImpl; ...} = import "bar"

foo = Foo.new()
foo.public()

Restricted Public Variables

Variable visibility is not limited to complete public/private. You can also publish with restrictions.

# foo.er
.record = {
     .a = {
         .(.record)x = 0
         .(module)y = 0
         .z = 0
     }
     _ = .a.x # OK
     _ = .a.y # OK
     _ = .a.z # OK
}

_ = .record.a.x # VisibilityError
_ = .record.a.y # OK
_ = .record.a.z # OK
foo = import "foo"
_ = foo.record.a.x # VisibilityError
_ = foo.record.a.y # VisibilityError
_ = foo.record.a.z # OK

Previous | Next