The root of Ozark is a class. Everything outside a method is declarative, and everything inside is imperative.
Strictly speaking, Ozark is a file type definition schema paired with a language for expressing imperitive instructions within a method.
Running a program in Ozark consists of creating an instance of a class, and then calling a method on that instance. See File Structure for more details about how files are structured in Ozark.
There are no global pointers or import statements. Pointers only exist within a method or as a property of a class.
inheritance Person property @calculator: Calculator property @cellPhone: Telephone extension setup & calculator: Calculator, & cellPhone: Telephone assign calculator to @calculator assign cellPhone to @cellPhone method clockIn time: implied Time @timeLog addEntry time, type: .clockIn method clockOut time: implied Time @timeLog addEntry time, type: .clockOut
All methods are instance methods. The object-oriented philosophy suggests that state be tightly integrated with functionality and stored within objects, and within Ozark, that's always the case.
These object-oriented constraints are uniquely valuable to Ozark. Developers who inherit legacy code in other languages often find that the underlying object-oriented structure was abandoned in certain places for convenience, maybe with a global variable or misuse of the singleton pattern. When you work on someone else's Ozark code, you won't worry about that. This is one of the ways that Ozark has been built for readability.
Ozark also avoids techniques that don't truly adhere to the principles of OO development. Static methods are one example. Another is the noticable lack of an object's ability to call its own methods.
The lack of a self reference and of unnamed code blocks forces inheritance stacks to be tall, methods to be short, and software to be built with dependency injection. This results in a lot of small, hyper-focused classes.
method swing pitch: Pitch pitch ball -> ball ball getHit probability: 0.3
property @ball: Ball? method throw target: BaseballPlayer target catch @ball clear @ball method catch ball: implied Ball assign ball to @ball
inheritance SportsPlayer property @offense: Offense property @defense: Defense extension setup create Offense; assign to @offense; setup create Defense; assign to @defense; setup method bat pitcher: Pitcher pitcher pitch -> pitch @offense swing pitch
Instructions aren't expressions
Lines of code don't have a value to be computed behind the scenes. Instead, you explicitly define all steps in the order of execution. With this constraint, it's easier to visualize complexity of a method.
inheritance GameAsset property @scene: GameScene extension setup create GameScene; assign to @scene; setup method processScene @scene evaluateActions @scene simulatePhysics @scene update
Many object-oriented languages define their classes through a set of imperative statements. In Ozark, the only imperative statements you'll find are inside of a method; Everything else is written as a declaration.
inheritance Driver property @car: RaceCar property @race: Race extension setup car: RaceCar create Race; assign to @race; setup assign car to @car method race track: RaceTrack, start: StartEvent @race enter track @race start @car @race! start
No return types for methods, just inputs and outputs
Rather than evaluating like an expression, a method is an action by an object, and has any number of inputs and/or outputs.
In Ozark, declaration order, indentation, spacing and naming conventions are all important.
Ozark code looks uniform, which makes it readable. It also means that an IDE can quickly parse Ozark into a logical model.
A code-generating application can use an Ozark file as a save format, much like Adobe Photoshop uses .psd files. Even better, this saved Ozark file can be opened, read, and edited by hand, then saved and read back into the code-generating application!
Strongly typed with no casting
Ozark is a strongly-typed language. There's no typecasting, only coersion between some primitive types like
inheritance Color method rgbValue -> rgbValue: implied Number, Number, Number assign 0.0, 0.0, 0.7 to rgbValue
method ! color: Color, sheet: Paper -> document: implied Paper color rgbValue -> rgbValue sheet rgbFill rgbValue assign sheet to document
inheritance Appliance property @paperTray: [Paper] property @print: Print extension setup create Print; assign to @print; setup create [Paper]; assign to @paperTray; setup | repeat 100 times method rgbPrint color: Color -> document: Paper? with @paperTray extract @paperTray[-1] -> sheet, assign to @paperTray @print! color: color, sheet: sheet -> assign to document
Small scopes with no globals
When coding in Ozark, the current working scope is always small. The only mutable pointers are either properties of a class, outputs of the current method, or deferred inputs to a dispatched method. There are no globals. Pointers only exist to store the state of an object, and to connect the inputs & outputs of methods. Think of them as values that are handed from one method call to another until they are stored safely as properties of objects.