Constants

Ki provides three main ways to define constants.

range Constants

Ranges are very useful constants.

range DayOfWeek [0..6]

range SingleEvens [2..8, 2]

fn main() {
    val day: DayOfWeek(1)
    val exercised: array([true, true, true, true, true, false, false])

    if exercised[day] {
        echo("Hey, I exercised!")
    }
    else {
        echo("Awww, I'll do better next time!")
    }
}

Normally, accessing exercised[day] would require a with statement, like with exercised[day] as exercised. However, because the indexing variable, "day", is an instance of DayOfWeek, and DayOfWeek's range never exceeds the length of exercised, no with statement is necessary.

Therefore, ranges should be used wherever possible. They are safer than bare numeric types, and they make working with sequential types much easier.

Instances of ranges are immutable, so using var instead of val (as in var DayOfWeek) is a compile-time error.

const Constants

const defines a range of named constants. These constants can be of any type, primitive or composite, but all entries must have the same type.

const FileModeString(str) {
    Read:             "r"
    Write             "w"
    Execute:          "x"
    ReadWrite:        "rw"
    ReadExecute:      "rx"
    WriteExecute:     "wx"
    ReadWriteExecute: "rwx"
}

There are limited means by which a composite type can be instantiated. Each entry of a const has access to its :this:name, :this:count, and :this:index, i.e. the third entry "ReadExecute" could use :this:name, :this:count or :this:index in a call to its initializer.

range MonthOfYear [1..12]

range DaysInMonth [28..31]

type Year (uint) {
    pred is_leap_year {
        return ((self % 4) == 0)
    }

    get days (DaysInYear) {
        if (self.is_leap_year) {
            return 366
        }
        else {
            return 365
        }
    }
}

type Month (MonthOfYear) {
    val name: *str

    @static
    fn from_month_of_year_and_name(MonthOfYear month, str *name) {
        :super:value = month
        self.name = name
    }

    fn days(Year year) (DaysInMonth) {
        switch (self) {
            case 2 {
                if (year.is_leap_year) {
                    return 29
                }

                return 28
            }
            case 4 {
                return 30
            }
            case 6 {
                return 30
            }
            case 9 {
                return 30
            }
            case 11 {
                return 30
            }
            default {
                return 31
            }
        }
    }
}

const Months (Month) {
    January(:this:count, :this:name)
    February(:this:count, :this:name)
    March(:this:count, :this:name)
    April(:this:count, :this:name)
    May(:this:count, :this:name)
    June(:this:count, :this:name)
    July(:this:count, :this:name)
    August(:this:count, :this:name)
    September(:this:count, :this:name)
    October(:this:count, :this:name)
    November(:this:count, :this:name)
    December(:this:count, :this:name)
}

This long-ish example shows advanced usage of const, where a little reflection is used to simplify initialization.

To Do: How to specify this initializer for all entries?

flag Constants

flag blocks are like const blocks, except each represents a separate boolean state.

const FileModeString {
    Read:             "r"
    Write             "w"
    Execute:          "x"
    ReadWrite:        "rw"
    ReadExecute:      "rx"
    WriteExecute:     "wx"
    ReadWriteExecute: "rwx"
}

flag FileMode {
    Read
    Write
    Execute

    fn to_file_mode_string() (FileModeString) {
        switch (self) {
            case Read {
                return FileModeString.Read
            }
            case Write {
                return FileModeString.Write
            }
            case Execute {
                return FileModeString.Write
            }
            case Read | Write {
                return FileModeString.ReadWrite
            }
            case Read | Execute {
                return FileModeString.ReadExecute
            }
            case Write | Execute {
                return FileModeString.WriteExecute
            }
            case Read | Write | Execute {
                return FileModeString.ReadWriteExecute
            }
        }
    }
}