Skip to content
FutureBlitz

    Install

    Modules and packages, 3 of 6

    Visibility

    Decide who sees each declaration, with Private, Protected, Package, and Public.

    What you’ll build

    A small package with a Shelf Class that holds a limited number of book titles. Its titles list stays private, a Class extending it can change how many it holds, a report in another file of the package reads the titles, and the main file uses only what’s Public. Each of the four visibilities appears once.

    VisibilitySeen by
    Private, the defaultthe file that declares it
    Protectedthat file, and the code of the Classes that extend its Class
    Packageevery file of its package
    Publicevery file that imports it

    Private by default

    A declaration with no visibility is Private: only its own file sees it. Members of Classes and Structs are Private too unless they say otherwise. Shelf’s titles field has no visibility, so code in other files can’t reach into the array and add a title past the limit.

    project.toml
    [project]
    name = "books"
    version = "0.1.0"
    shelf.fb
    Import std.core
    Import std.collections
    
    Public Class Shelf
        Field titles: String[] = []
        Protected Field capacity: Int = 3
    
        Public Method Add(title: String): Bool
            If titles.Count >= capacity Then Return False
            titles.Add(title)
            Return True
        End Method
    
        Public Property Count: Int
            Get => titles.Count
        End Property
    
        Package Method Titles(): String[] => titles.Clone()
    End Class
    
    Public Class TallShelf Extends Shelf
        Public New()
            capacity = 5
        End New
    End Class
    report.fb
    Import std.core
    Import std.text
    Import books.shelf
    
    Package Function Report(shelf: Shelf): String => ", ".Join(shelf.Titles())
    main.fb
    Import std.core
    Import books.shelf
    Import books.report
    
    Local shelf = TallShelf()
    For title In ["Dune", "Emma", "Ulysses", "Beloved"]
        Print("{title}: {shelf.Add(title)}")
    End For
    Print("{shelf.Count} books: {Report(shelf)}")
    Output
    Dune: True
    Emma: True
    Ulysses: True
    Beloved: True
    4 books: Dune, Emma, Ulysses, Beloved

    Titles and Report are Package: report.fb calls Titles, and main.fb calls Report, since all three files are the books package’s. Shelf, TallShelf, Add, Count, and TallShelf’s New are Public, which is what any file that imports books.shelf can use. Marking only what other code needs as Public keeps the rest free to change without breaking it.

    Code outside the file

    main.fb can’t use shelf.titles, which is Private, or capacity, which is Protected:

    project.toml
    [project]
    name = "books"
    version = "0.1.0"
    shelf.fb
    Import std.core
    Import std.collections
    
    Public Class Shelf
        Field titles: String[] = []
        Protected Field capacity: Int = 3
    
        Public Method Add(title: String): Bool
            If titles.Count >= capacity Then Return False
            titles.Add(title)
            Return True
        End Method
    
        Public Property Count: Int
            Get => titles.Count
        End Property
    
        Package Method Titles(): String[] => titles.Clone()
    End Class
    
    Public Class TallShelf Extends Shelf
        Public New()
            capacity = 5
        End New
    End Class
    main.fb
    Import std.core
    Import books.shelf
    
    Local shelf = TallShelf()
    Print(shelf.titles.Count)
    Print(shelf.capacity)
    $ fb run
       Compiling books v0.1.0
    main.fb:5:13: error: 'titles' is private to shelf.fb
      |
    5 | Print(shelf.titles.Count)
      |             ^^^^^^
    shelf.fb:5:11: note: declared here
      |
    5 |     Field titles: String[] = []
      |           ------
    main.fb:6:13: error: 'capacity' is Protected in shelf.fb, so only that file and the Classes extending Shelf can use it
      |
    6 | Print(shelf.capacity)
      |             ^^^^^^^^
    shelf.fb:6:21: note: declared here
      |
    6 |     Protected Field capacity: Int = 3
      |                     --------
    error: couldn't build books v0.1.0 because of 2 errors

    capacity is Protected, so TallShelf, which extends Shelf, can set it to 5 in its New, and four books fit. main.fb isn’t a Class extending Shelf, and its error says who can. Protected goes only on a Class’s members, and a Class that extends Shelf in another file, or another package, sees it too.

    Another package

    books has a main file, so it’s a program, which no other package can depend on. A package that others use is a library. Here the program hall depends on the library shelves, in a folder of its own, and sees only what shelves makes Public: its Package declarations are its own helpers, not part of what it offers.

    project.toml
    [project]
    name = "hall"
    version = "0.1.0"
    
    [dependencies]
    shelves = { path = "shelves" }
    shelves/project.toml
    [project]
    name = "shelves"
    version = "0.1.0"
    type = "library"
    shelves/labels.fb
    Import std.core
    
    Public Function Label(name: String, count: Int): String => "{Prefix()}{name}: {count} books"
    
    Package Function Prefix(): String => "Shelf "
    main.fb
    Import std.core
    Import shelves
    
    Print(Label("Fiction", 4))
    Print(Prefix())
    $ fb run
       Compiling hall v0.1.0
    main.fb:5:7: error: 'Prefix' is Package in shelves/labels.fb, so only the package shelves can use it
      |
    5 | Print(Prefix())
      |       ^^^^^^
    shelves/labels.fb:5:18: note: declared here
      |
    5 | Package Function Prefix(): String => "Shelf "
      |                  ------
    error: couldn't build hall v0.1.0 because of 1 error

    Label works from hall, and uses Prefix inside shelves, but hall can’t call Prefix itself. A file outside any package, run with fb run file.fb, is in a package with the files it imports by path.

    What you learned

    • Declarations are Private unless they say otherwise, and Private means the declaring file.
    • Protected members are also seen by the Classes that extend their Class.
    • Package declarations are seen by every file of their package, and by no other package.
    • Public declarations are seen by every file that imports them.

    Report an issue with this page or with FutureBlitz on GitHub.