Styleguide

This guide captures the conventions used across the Zirric standard library and
proposals. Favor clarity and consistency over cleverness.

General principles

  • Prefer descriptive names over abbreviations.
  • Keep files small and focused.
  • Match existing style in nearby modules.

Modules

Zirric favors many small modules over a few large ones. Keep each module focused
and consider hiding implementation details in an internal module.

Module names use snake_case but should avoid underscores when possible. Names
should be short, expressive, and usually plural. Keep module names aligned with
the declarations they contain.

// good
module http
module json
module strings
module reflect // reflect.typeOf
// bad
module http_client
module json_parser
module string_utils
module reflection // reflection.typeOf
                                

If a filename or its declarations could clash with another module, declare the
module explicitly at the top of the file:

module my_module
                                    

Data types

Data type names use PascalCase. Prefer nouns, pluralize only when the type is
a collection, and avoid prefixing the module name.

Witness types describe capabilities and may be suffixed with able or ible.

// good
data Person
data People
data Printable // witness type
data Error // in module http
// bad
data person
data people
data Printer // witness type
data HttpError // in module http
                                        

Functions

Function names use camelCase. Names should read as verbs or verb phrases and
form a sentence with the module name.

// good
print
printLine
reflect.typeOf
// bad
print_line
reflect.reflectType
                                            

Variables

Variable names use camelCase. Names should be as long as their scope: short
names for tight scopes, longer names for shared or public values. Reuse common
names in well-known patterns.

// good
let name = "John"
let person = Person("John", 42)
let err = http.Error("Not found")
func printPersonName(p) {
    print(p.name)
}
// bad
let n = "John"
let p = Person("John", 42)
let error = http.Error("Not found")
func printName(n) {
    print(n.name)
}
                                                

Annotations

Annotations use PascalCase and should describe the property they declare.

// good
annotation Returns
annotation HasKey
annotation IsOptional
// bad
annotation Return
annotation Key
annotation Optional
                                                

Enums

Enum names use PascalCase. Choose singular nouns unless the enum itself is a
collection. For witness enums, describe the capability, optionally with a
Witness suffix.

// good
enum Stateful
enum JuristicPerson
enum FunctorWitness
// bad
enum StateOrStore
enum JuristicPersons
enum Functor
                                                    

If the cases inside the enum are more relevant than the enum name itself, define
them at top level instead of nesting.

// good
enum Optional {
    data Some { value }
    data None
}
enum Maybe {
    Optional
    Any
}
// bad
enum Maybe {
    enum Optional {
        data Some { value }
        data None
    }
    Any
}
                                                        

Imports and layout

  • Group imports together near the top of the file.
  • Keep one declaration per block to make annotations and docs obvious.
  • Prefer blank lines to separate logical sections.