Organization
Kestrel programs are made of modules. A module is a unit of code with its own namespace; modules are made of files; files declare which module they belong to and import names from other modules.
Modules
Every .ks file starts with a module declaration:
module Game.Player
// ...declarations live here
The module name is dotted to suggest hierarchy — Game.Player is conceptually inside Game. Hierarchy here is naming convention; modules are otherwise flat. A program can have many files in one module; all declarations across those files share the namespace.
A common shape for a small project:
src/
main.ks — module Main
game.ks — module Game
player.ks — module Game.Player
enemy.ks — module Game.Enemy
Visibility
Four levels:
public struct Account {
public func openToEveryone() {}
internal func sameModuleOnly() {} // default
fileprivate func sameFileOnly() {}
private func sameDeclarationOnly() {}
}
internal is the default — if you don't write a modifier, that's what you get. public is opt-in: anything you want callable from another module needs public on it. fileprivate restricts to the file it's declared in. private restricts to the enclosing declaration — an extend block outside the struct body can't access private fields, but can access fileprivate ones.
The same modifiers apply to types, fields, methods, and protocol requirements. A public func on an internal struct is still only reachable from the module — visibility is the minimum of the chain.
Imports
import brings names from another module into scope:
import std.io.stdio.println
import std.collections.Dictionary
You can import a specific name (as above) or a whole module:
import std.collections // brings the module in; reference as Dictionary, Array, ...
The Kestrel standard library auto-imports its most-used names — Int, String, Bool, Optional, Result, Array, etc. You shouldn't need to write import std.num.Int manually; if a basic name resolves, it's because the prelude already imported it.
Import only what you use. Wildcards aren't supported; if you want every name from a module, import the module itself and use the prefix.
A complete example
// game/player.ks
module Game.Player
import std.io.stdio.println
public struct Player {
public let name: String
public var hp: Int
}
extend Player {
public mutating func takeDamage(amount: Int) {
self.hp = self.hp - amount;
println("\(self.name) takes \(amount) damage");
}
}
<!-- sample: skip -->
// main.ks
module Main
import Game.Player.Player
@main
func main() {
var p = Player(name: "Morgana", hp: 100);
p.takeDamage(25);
}