Packages¶
Use the package keyword to define a package. Use export to make a package available, and import to bring a package into scope. Methods defined in a package are accessed using a fully qualified name (package::method).
Defining and Using a Package¶
package greeter
fn greet(name)
println("Hello, ${name}.")
end
end
package excited_greeter
fn greet(name)
println("Hello, ${name}!")
end
end
# Make the packages available.
export "greeter"
export "excited_greeter"
# Invoke their methods.
greeter::greet("World")
excited_greeter::greet("World")
Output:
File-Based Packages¶
To organize packages into separate files, define the package in its own .kiwi file and call export at the end of the file. Then use include in consuming scripts to load the file.
lib/greeter.kiwi
main.kiwi
import vs export¶
Both import and export activate a package and make its members accessible. By convention:
- Use
export "name"inside the package file (self-export). - Use
import "name"in a consuming script when the package is already in scope.
package math_utils
fn square(n)
return n * n
end
end
export "math_utils"
println math_utils::square(5) # 25
Nested Packages¶
Packages support namespaced names using ::.
package app::utils
fn greet(name)
println("Hello, ${name}!")
end
end
export "app::utils"
app::utils::greet("World")
Extending Built-in Types¶
When a package's name matches a built-in type name and a function's first parameter is typed as that type, Kiwi automatically makes the function callable as a method on any value of that type.
package list
fn sum_positive(_list: list): integer
_list.filter(do (n) => n > 0).sum()
end
end
export "list"
nums = [-3, 1, -1, 4, 2]
println nums.sum_positive() # 7
The function is reachable both ways:
How it works¶
The dispatch rule has three parts:
- Package name matches a built-in type:
list,string,date,integer,float,boolean,hashmap, orbytes. - First parameter carries a matching type hint (e.g.
_s: string). - Caller invokes it as
value.func(args...)— Kiwi routes this topackage::func(value, args...).
If the first parameter has no type hint, or the package name does not match a type, the function is only reachable via the package::func(...) form.
Extending string¶
package string
fn word_count(_s: string): integer
_s.trim().split(" ").size()
end
fn shout(_s: string): string
_s.uppercase() + "!!!"
end
end
export "string"
println "hello world".word_count() # 2
println "kiwi".shout() # KIWI!!!
Extending integer¶
package integer
fn factorial(_n: integer): integer
return 1 when _n <= 1
_n * (_n - 1).factorial()
end
end
export "integer"
println (5).factorial() # 120
println (10).factorial() # 3628800
Extending date¶
The standard library's date package uses this pattern to add arithmetic and accessors to every date value:
# lib/date.kiwi (stdlib)
package date
fn add_days(_dt: date, n: integer): date
time::add_days(_dt, n)
end
end
export "date"
After loading, any date value gains .add_days():
Standard library type extensions¶
The following stdlib packages use this pattern:
| Package | Extends | Key additions |
|---|---|---|
list |
list |
all, _any, _none, one, reject, find, first_where, last_where, sum_integer, sum_float, iterator |
string |
string |
base64encode, base64decode, slug, padstart, padend, center, mirror, isalpha, isnumeric, islower, isupper |
date |
date |
add_days, add_months, add_years, add_hours, year, month, day, weekday, and more |
Notes¶
- The first parameter name is conventional — it is not special.
_list,lst,selfall work equally. - If two loaded packages extend the same type with the same function name, the last one loaded wins.
- Type extension only applies to built-in types. Struct methods are defined inside the struct body using
fn. - Functions with no type hint on the first parameter are still callable as
pkg::func(value)but will not be dispatched as instance methods.