Inko 0.15.0 released
We're pleased to announce the release of Inko 0.15.0. This release includes support for automatically formatting source code, generating documentation from source code, support for handling Unix signals, and much more.
Table of contents
- Automatic formatting of source code
- Generating documentation from source code
- Support for handling Unix signals
- Parsing of command-line options
- Support for compile-time variables
- Improvements to interacting with C code
- Renaming of error handling methods
- The drop order of fields and constructor arguments is changed
- TCP_NODELAY is enabled for TCP sockets
- Syntax changes
- LLVM 16 is now required
For the full list of changes, refer to the changelog.
A special thanks to the following people for contributing changes included in this release:
We'd like to thank the NLnet foundation for sponsoring part of the work that went into this release.
Automatic formatting of source code
Thanks to the NLnet foundation for sponsoring the development of this feature.
Inko 0.15.0 introduces the inko fmt
command, capable of formatting one or more
source files according to the Inko style guide. This command can either be used
manually from the command-line, or from your editor using the appropriate plugin
(e.g. conform.nvim for NeoVim). For
example, we can automatically format messy code such as this:
import std.stdio(STDOUT)
import std.net.ip(IpAddress)
class async Main {
fn async main {
let addr = IpAddress.v4(
127,0,0,
1
)
STDOUT.new
.print(addr
.to_string)
}
}
Into this:
import std.net.ip (IpAddress)
import std.stdio (STDOUT)
class async Main {
fn async main {
let addr = IpAddress.v4(127, 0, 0, 1)
STDOUT.new.print(addr.to_string)
}
}
Inko's formatter is an opinionated formatter and offers no configuration
settings to adjust, ensuring that every project using inko fmt
uses the same
style. Some might argue in favour of being able to configure certain aspects
(e.g. the line length), but the ability to do so only leads to constant
bike-shedding.
Users can still control the formatting somewhat, as the formatter retains empty lines in expression bodies (collapsing multiple empty lines into a single one). This allows you to visually separate certain blocks of code if deemed beneficial.
See the
documentation
for more details on how to use the new inko fmt
command.
See commit 215b63d for more details, and this article for details on the algorithm used by the formatter.
Generating documentation from source code
Thanks to the NLnet foundation for sponsoring the development of this feature.
Starting with Inko 0.15.0, you can generate documentation from your source code.
The inko doc
command is used to generate a list of JSON files containing
information about the various symbols (e.g. classes) of a program. A separate
program is then used to convert these JSON files into the desired format.
idoc is an official program used to convert
these JSON files to a static HTML website.
idoc is also used to generate the documentation for Inko's standard library, which is found at the following places:
To use idoc in your own project, run idoc
and the resulting website is found
at ./build/idoc/public
. To include documentation for dependencies, run idoc
--dependencies
. In the future we'll also provide a platform to host
documentation for Inko packages, such that you don't need to generate this
yourself. See this issue for
more details.
For more information about generating documentation (such as how to install idoc), refer to the documentation, or take a look at commit cefa664.
Support for handling Unix signals
Inko 0.15.0 introduces the type
std.signal.Signal
.
This type is used to block the current process (not the OS thread) until a given
Unix signal is received. For example, to wait for the SIGUSR1
signal, one uses
it as follows:
import std.signal (Signal)
class async Main {
fn async main {
Signal.User1.wait
}
}
Signal support is implemented by using a dedicated thread that calls
sigwait(3)
, with all the registered signals, rescheduling the appropriate
processes when their desired signal is received.
For more information, refer to the following:
- The documentation of
std.signal.Signal
- The documentation on how signal handling is implemented in the runtime
- Commit 1d4bad2
Parsing of command-line options
The standard library now includes the module
std.optparse
,
used for parsing command-line options. For example, to parse a --help
option
one writes the following:
import std.env
import std.optparse (Options)
class async Main {
fn async main {
let opts = Options.new
opts.flag('h', 'help', 'Show this help message')
let matches = opts.parse(env.arguments).get
}
}
See commit 0fb6d0a for more details.
Support for compile-time variables
If a constant is defined using let pub
and of type String
, Int
or Bool
,
you can overwrite its value at compile-time using inko build --define
. This
allows you to change certain parts of a program without having to change its
source code, such as the directory it loads certain files from. For example:
let pub PATH = '/usr/share/example'
class async Main {
fn async main {}
}
To change PATH
to /usr/local/share/example
, we compile the program as
follows:
inko build --define main.PATH=/usr/local/share/example
For more information, refer to the documentation and commit 3298ae2 for more details.
Improvements to interacting with C code
The Main
process now always runs on the main OS thread. This makes it possible
to interact with C code that uses thread-local state and expects code to run on
the same thread, such as most GUI frameworks.
In addition, module methods defined using the extern
keyword can be passed as
callbacks to C functions using the syntax mut method_name
. For example:
# This guarantees this method uses the C calling convention.
fn extern example(value: Int) -> Int {
value
}
class async Main {
fn async main {
let pointer_to_method = mut example
}
}
Here pointer_to_method
is a pointer to the example
method, not a borrow.
See commits 0f68a92 and 01bd04e for more details.
Renaming of error handling methods
The following methods are renamed:
unwrap
(e.g.Option.unwrap
) is renamed toget
(e.g.Option.get
)unwrap_or
is renamed to justor
unwrap_or_else
is renamed toor_else
expect
is renamed toor_panic
This means that instead of this:
let x = Option.Some(42)
x.expect('a Some is expected')
You now write this:
let x = Option.Some(42)
x.or_panic('a Some is expected')
The drop order of fields and constructor arguments is changed
Instead of dropping fields and constructor arguments in definition order, they're now dropped in reverse-definition order to match the order used when dropping local variables. If you are experiencing drop panics when upgrading to Inko 0.15.0, you'll likely need to change the definition order of the relevant fields to resolve this.
See commit 9ce4f9e for more details.
TCP_NODELAY is enabled for TCP sockets
When using
std.net.socket.TcpClient
and
std.net.socket.TcpServer
,
the TCP_NODELAY
option is set automatically. This option is enabled because
not enabling it can lead to latency issues when using TCP sockets. In the rare
case that this option must be disabled, one can do so using
std.net.socket.Socket.no_delay=
.
See commit 740682c and this article for more details.
Syntax changes
Strings
Whitespace in string literals can no longer be escaped like so:
let x = 'foo \
bar'
Before 0.15.0 this would result in the string value foo bar
, but starting with
0.15.0 this is now a syntax error. The reason for this is that the presence of
these escapes complicates the parser too much, it makes automatic code
formatting difficult, and it's not that useful as you can do the following
instead:
let x = 'foo ' + 'bar'
See commit dc87e18 for more details.
Imports
The import syntax is changed such that a .
is no longer needed between the
module and the symbol list. In addition, symbols must always be surrounded by
parentheses, even when importing only a single symbol:
# Before:
import a.b.c.A
import a.b.c.(A, B)
# After:
import a.b.c (A)
import a.b.c (A, B)
See commit 41a0b3e for more details.
Class literal syntax
The syntax to create an instance of a class is changed, instead of this:
class Person {
let @name: String
}
Person { @name = 'Alice' }
You write this instead:
class Person {
let @name: String
}
Person(name: 'Alice')
The old syntax is still supported and inko fmt
updates it to the new syntax
automatically, but support will be removed in the next release.
See commit b7bcf46 for more details.
Trailing closure syntax
Inko supported the following syntax for closures, provided the closure is the last argument:
foo(10, 20) fn { ... }
Support for this syntax will be removed in 0.16.0, and inko fmt
changes it to
the following:
foo(10, 20, fn { ... })
This new syntax makes both parsing and formatting (much) simpler compared to the old syntax.
LLVM 16 is now required
Inko 0.15.0 requires LLVM 16, and no longer supports LLVM 15. We plan to upgrade to LLVM 17 as part of the next release.
See commit a35d4ec for more details.
Following and supporting Inko
If Inko sounds like an interesting language, consider joining the Discord server. You can also follow along on the /r/inko subreddit. If you'd like to support the continued development of Inko, please consider donating using GitHub Sponsors.