Skip to content
FutureBlitz

    Install

    Your own types, 8 of 8

    Extend

    Add Methods to any type, the built-in ones included, and make an existing type implement an Interface.

    What you’ll build

    A few helpers that read as if they were part of the language: "level".IsPalindrome(), 3.Times(...), and scores.Second(). Then a Struct that becomes printable without changing its declaration. An Extend block adds Methods to a type that’s already declared, anywhere, even a built-in type or std’s.

    Methods for String and Int

    Extend String adds Methods to every String. Inside them, Self is the value, and the type’s own members are used by name, as in the type’s own Methods.

    helpers.fb
    Import std.core
    Import std.text
    
    Extend String
        Method Shout(): String => ToUpper() + "!"
    
        Method IsPalindrome(): Bool
            Local letters = ToLower().Chars
            For i = 0 Until letters.Count / 2
                If letters[i] != letters[letters.Count - 1 - i] Then Return False
            End For
            Return True
        End Method
    End Extend
    
    Extend Int
        Method IsEven(): Bool => Self % 2 == 0
    
        Method Times(action: Func(Int))
            For i = 1 To Self
                action(i)
            End For
        End Method
    End Extend
    
    Print("hello".Shout())
    Print("Level".IsPalindrome(), "Hello".IsPalindrome())
    Print(4.IsEven(), 7.IsEven())
    3.Times(Function(i) => Print("pass {i}"))
    Output
    HELLO!
    True False
    True False
    pass 1
    pass 2
    pass 3

    Methods for every array

    Extend T[] adds Methods to arrays of every type, with T standing for the element type. Here Second gives the second element, or Null for an array of fewer than two.

    helpers.fb
    Import std.core
    
    Extend T[]
        Method Second(): T? => Count > 1 ? Self[1] : Null
    End Extend
    
    Print([10, 20, 30].Second() ?? 0)
    Print(["only"].Second() ?? "none")
    Output
    20
    none

    Implementing an Interface

    Extend Point Implements Printable makes Point implement Printable, with the Methods the Extend gives, so a Point can be printed. This works on types from other files and packages too, whose declarations you can’t change.

    helpers.fb
    Import std.core
    
    Struct Point
        Field X: Int
        Field Y: Int
    End Struct
    
    Extend Point Implements Printable
        Method ToString(): String => "({X}, {Y})"
    End Extend
    
    Local corners = [Point(0, 0), Point(4, 0), Point(4, 3)]
    Print(corners)
    Output
    [(0, 0), (4, 0), (4, 3)]

    An Extend adds behavior, never data: it can’t add fields or a New, since how a type is stored and made is the type’s own. Its Methods can’t replace one the type has, and aren’t virtual, so nothing overrides them. An Extend’s Methods are seen only in its own module and where that module is imported, so one file’s helpers don’t change what another file’s code means. The Interfaces an Extend adds are different: they hold everywhere, since a type is Printable or isn’t, for the whole program.

    What you learned

    • Extend T adds Methods to an existing type, built-in types included.
    • Inside an Extend, Self is the value, and the type’s members are used by name.
    • Extend T[] adds Methods to every array, with T as the element type.
    • Extend T Implements I makes an existing type implement an Interface.
    • An Extend adds no fields or New, and replaces nothing the type has.

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