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.
| Visibility | Seen by |
|---|---|
Private, the default | the file that declares it |
Protected | that file, and the code of the Classes that extend its Class |
Package | every file of its package |
Public | every 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]
name = "books"
version = "0.1.0"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 ClassImport std.core
Import std.text
Import books.shelf
Package Function Report(shelf: Shelf): String => ", ".Join(shelf.Titles())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)}")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]
name = "books"
version = "0.1.0"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 ClassImport std.core
Import books.shelf
Local shelf = TallShelf()
Print(shelf.titles.Count)
Print(shelf.capacity)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]
name = "hall"
version = "0.1.0"
[dependencies]
shelves = { path = "shelves" }[project]
name = "shelves"
version = "0.1.0"
type = "library"Import std.core
Public Function Label(name: String, count: Int): String => "{Prefix()}{name}: {count} books"
Package Function Prefix(): String => "Shelf "Import std.core
Import shelves
Print(Label("Fiction", 4))
Print(Prefix())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.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.