Skip to content
FutureBlitz

    Install

    Your own types, 5 of 8

    Enums

    Name a fixed set of values, handle each in a Select that the compiler checks, and combine flags.

    What you’ll build

    A blog’s article workflow: each article has a status, from draft to archived, and each editor has a set of permissions. An Enum names each possible value, so the program can’t hold a status that doesn’t exist.

    Declaring an Enum

    An Enum lists its members, separated by commas or on lines of their own. Each member is used through the Enum’s name, like Status.Review, and prints as its name. Behind each member is an integer, from 0 up, unless a member is given its own value.

    articles.fb
    Import std.core
    
    Enum Status
        Draft
        Review
        Published = 10
        Archived
    End Enum
    
    Local status = Status.Review
    Print(status, status == Status.Review)
    Print(Status.Draft As Int, Status.Archived As Int)
    Print(10 As Status)
    Output
    Review True
    0 11
    Published

    Archived follows Published, so it’s 11. As turns a member into its integer and back. An integer that isn’t one of the Enum’s values is an error when the compiler can see it, as in 3 As Status, and a panic when the program runs.

    Select over every member

    A Select on an Enum must handle every member, or have a Default. Leaving a member out is an error that names it, so adding a member later points at each Select that has to handle it. Since this Select covers every member, the function returns a value on every path.

    articles.fb
    Import std.core
    
    Enum Status
        Draft, Review, Published, Archived
    End Enum
    
    Function Label(status: Status): String
        Select status
            Case Status.Draft
                Return "being written"
            Case Status.Review
                Return "waiting for an editor"
            Case Status.Published
                Return "live"
            Case Status.Archived
                Return "taken down"
        End Select
    End Function
    
    Local statuses = [Status.Draft, Status.Published, Status.Archived]
    For status In statuses
        Print("{status:<10} {Label(status)}")
    End For
    Output
    Draft      being written
    Published  live
    Archived   taken down

    Without the Case for Status.Archived, the program doesn’t build. fb names the member left out, and says too that Label then has a path that returns nothing:

    articles.fb
    Import std.core
    
    Enum Status
        Draft, Review, Published, Archived
    End Enum
    
    Function Label(status: Status): String
        Select status
            Case Status.Draft
                Return "being written"
            Case Status.Review
                Return "waiting for an editor"
            Case Status.Published
                Return "live"
        End Select
    End Function
    
    Print(Label(Status.Draft))
    $ fb run articles.fb
       Compiling articles.fb
    articles.fb:7:10: error: 'Label' doesn't return a value on every path
      |
    7 | Function Label(status: Status): String
      |          ^^^^^
    articles.fb:8:12: error: Select doesn't cover Status.Archived; add a Case for each, or a Default
      |
    8 |     Select status
      |            ^^^^^^
    error: couldn't build articles.fb because of 2 errors

    Flags

    A @Flags Enum’s members are bits, 1, 2, 4, and so on, so a value can hold several at once. | combines them, and Has asks whether a value has all the flags given. A combination prints as its members’ names. Since a @Flags value can be any combination, a Select on one always needs a Default.

    articles.fb
    Import std.core
    
    @Flags Enum Access
        Read, Write, Publish, Delete
    End Enum
    
    Local author = Access.Read | Access.Write
    Local editor = author | Access.Publish
    Print(author, editor)
    Print(author.Has(Access.Write), author.Has(Access.Publish))
    Print(editor.Has(Access.Read | Access.Publish))
    Print(editor & ~Access.Write)
    Output
    Read | Write Read | Write | Publish
    True False
    True
    Read | Publish

    ~ flips every bit, so ~Access.Write is every flag but Write, and editor & ~Access.Write keeps the editor’s flags without Write.

    An Enum in a type

    An Enum is a value like a number, so it fits in a field. A field of one starts at the Enum’s zero: its first member, or no flags at all for a @Flags Enum.

    articles.fb
    Import std.core
    
    Enum Status
        Draft, Review, Published
    End Enum
    
    Class Article
        ReadOnly Field Title: String
        Field status: Status
    
        New(title: String)
            Title = title
        End New
    
        Method Advance()
            Select status
                Case Status.Draft
                    status = Status.Review
                Case Status.Review
                    status = Status.Published
                Case Status.Published
                    Print("{Title} is already published")
            End Select
        End Method
    End Class
    
    Local article = Article("Hello")
    For step = 1 To 3
        article.Advance()
        Print(article.status)
    End For
    Output
    Review
    Published
    Hello is already published
    Published

    What you learned

    • An Enum names a fixed set of values, each an integer underneath.
    • Members print as their names, and As converts to and from integers.
    • A Select on an Enum must cover every member or have a Default; on a @Flags Enum, it always has a Default.
    • @Flags members are bits that combine with |, and Has tests them.
    • An Enum’s zero is its first member, or no flags for a @Flags Enum.

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