Style guide
This document provides a high-level overview of Inko's style guide, covering
topics such as when to wrap lines, what indentation to use, and more. This guide
is not a low-level specification of how to precisely format every syntax
construct, instead the inko fmt
command acts as the specification.
Using inko fmt
The inko fmt
command formats source files according to the Inko style guide.
You can use it to format specific files as follows:
inko fmt foo.inko bar.inko
You can also format the entire project like so:
inko fmt
Source code can also be passed through STDIN, in which case you need to use
inko fmt -
:
echo 'fn example() {}' | inko fmt -
In this case the output is written to STDOUT.
You can check for any files that need formatting using inko fmt --check
. This
won't update any files, instead it prints any files that need to be formatted
and exits with exit code 1 (or 0 if all files are formatted correctly).
Encoding
Inko source files must be encoded in UTF-8. The compiler does not support reading source files using a different encoding.
Line endings
Unix (\n
) line endings must be used at all times. Windows newlines (\r
) are
not supported.
Line length
Lines should be hard wrapped at 80 characters per line. If a string literal doesn't fit on a single line, it's fine to leave it as-is.
Indentation
Inko source code should be indented using 2 spaces per indentation level, not tabs. Different programs use different widths for tabs (sometimes with no way of changing this), potentially making source code harder to read. By using spaces only we prevent the accidental mixing of tabs and spaces, and ensure Inko code always looks consistent.
Naming
Types use PascalCase, such as ByteArray
and String
:
class AddressFormatter {}
Methods, local variables, instance attributes, and arguments all use snake_case
for naming, such as to_string
and write_bytes
:
Constants
Constants defined using let
use SCREAMING_SNAKE_CASE
, such as DAY_OF_WEEK
or NUMBER
:
let FIRST_DAY_OF_WEEK = 'Monday'
Argument names
Arguments should use human readable names, such as address
. Avoid the use of
abbreviations such as num
instead of number
. Every argument is a keyword
argument, and the use of abbreviations can make it harder for a reader to figure
out what the meaning of an argument is.
Predicates
When defining a method that returns a Boolean
, end the method name with a ?
:
fn allowed? -> Boolean {
# ...
}
This removes the need for prefixing your method names with is_
, such as
is_allowed
.
Traits
Traits should be a given a clear name such as ToArray
or Index
. Don't use
the pattern of [verb]-ble
such as Enumerable
or Iterable
.
Conversion methods
Methods that convert one type into another should be prefixed with to_
,
followed by a short name of the type. Examples include to_array
, to_string
,
to_coordinates
, etc. If a value is moved into another type, use into_
as
the prefix instead.
Arguments
When calling a method without arguments, leave out the parentheses:
[10, 20, 30].pop
If the last argument of a method call is a closure, format it like so:
[10, 20, 30].each(fn (number) {
# ...
})
When the number of arguments don't fit on a single line, place each argument on its own line and use a trailing comma for the last argument:
some_object.some_message_name(
10,
20,
30,
)
Named arguments
The use of named arguments is recommended whenever this enhances the readability of code. Take the following code for example:
'hello'.slice(0, 2)
Looking at this code, it's not clear what the values 0
and 2
are used for.
Using keyword arguments this becomes obvious:
'hello'.slice(start: 0, chars: 2)
When passing variables with the same name as the arguments, you can leave out named arguments:
# This is redundant.
'hello'.slice(start: start, chars: chars)
# This is fine.
'hello'.slice(start, chars)
Comments
Comments should be used to describe intent, provide examples, and explain certain decisions that might not be obvious to the reader. Comments should not be used to explain what the code does in its literal sense.
When documenting a type, constant, or method, the first line of the comment should be a short summary. This summary should be about one sentence long and describe the purpose of the item. For example:
# A Person can be used for storing details of a single person, such as their
# name and address.
class Person {
}
When documenting a module, start the comment on the first line of the module, before any imports:
# The documentation of the module goes here.
import std.stdio (STDOUT)
Imports
Imports should be placed at the top of a module, in alphabetical order:
import std.fs.file
import std.stdio.stdout
The symbols imported from a module should also be listed in alphabetical order.
If self
is imported, it should come first:
import std.fs.file (self, ReadOnlyFile)