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
}
}
}
}