Safe and concurrent object-oriented programming, without the headaches.

Inko is a statically-typed, safe, object-oriented programming language for writing concurrent programs. Thanks to the use of lightweight processes, concurrency is easily achieved and data races are impossible.

The compiler forces you to handle errors where they may occur, ensuring you never run into unexpected runtime errors again.

The syntax is easy to learn and consistent, instead of using many different keywords and symbols. If you have ever used a programming language that uses curly braces, you'll feel right at home.

import std::stdio::stdout

stdout.print('Hello, world!')
import std::process
import std::stdio::stdout

let proc = process.spawn {
  # This will print "Hello" to STDOUT.
  match(let message = process.receive) {
    as String -> { stdout.print(message) }
    else -> {}

object List!(T) {
  @values: Array!(T)

  static def new -> Self {
    Self { @values = }

  def push(value: T) -> T {

let list =

# This is OK:

# This will produce a type error:
import std::stdio::stderr

def withdraw(euros: Integer) !! String {
  euros.negative?.if_true {
    throw 'Invalid number of Euros!'

# "withdraw" might throw, so we must use the
# "try" keyword
try withdraw(euros: 5)

# We can also handle the error, if needed:
try {
  withdraw(euros: 5)
} else (error) {

# "try!" terminates the program upon
# encountering an error:
try! withdraw(euros: 5)
import std::fs::file
import std::stdio::stdout

let txt_file = try! file.read_only('text.txt')
let content = try! txt_file.read_string

import std::fs::file

let readme =
  try! file.read_only('')

# This will produce a type error, since
# the file is opened in read-only mode.

# This also won't work, because we can
# not remove a read-only file.
import std::test
import std::test::assert'Integer.+') do (group) {
  group.test('Summing two Integers') {
    assert.equal(1 + 2, 3)
import std::conversion::ToString
import std::stdio::stdout

object Person {
  @name: String

  static def new(name: String) -> Self {
    Self { @name = name }

impl ToString for Person {
  def to_string -> String {

let person ='Alice')

# This will print "Alice" to STDOUT:
let numbers =, 20, 30)

# Array.get() returns an Option type.
  .map do (n) { n.to_string } # => Some("30")
# Inko guarantees tail call elimination for
# tail recursive methods, so this method
# doesn't overflow the stack.
def fact(num: Integer, acc = 1) -> Integer {
  # This returns from the surrounding method. { return acc }

  fact(num - 1, acc * num)

fact(15) # => 1307674368000
import std::stdio::stdout

let mut number = 0

while({ number < 10 }) {
  stdout.print('This loop will run 10 times')
  number += 1

loop {
  stdout.print('This is an infinite loop.')
import std::stdio::stdout

let numbers =, 20, 30)

# "each" allows us to easily iterate over
# a collection:
numbers.each do (number) {

# Using "iter" we can obtain an external,
# composable iterator:
let new_numbers = numbers
  .map do (num) { num * 2 }

new_numbers # =>, 40, 60)
# This method yields values of type Integer
def numbers => Integer {
  yield 10
  yield 20

let generator = numbers

generator.resume # => Some(10)
generator.resume # => Some(20)
generator.resume # => None
def example(number: Integer) -> String {
  match(number) {
    1..10 -> { 'Yes!' }
    20 -> { 'Oh no!' }
    else -> { 'The number is not supported' }

example(10) # => 'Yes!'
example(20) # => 'Oh no!'
example(50) # => 'The number is not supported'
def example(type: Any) -> String {
  match(type) {
    as String -> { 'A String' }
    as Integer -> { 'An Integer' }
    else -> { 'Something else' }

example('foo') # => 'A String'
example(10)    # => 'An Integer'
import std::net::socket::TcpListener
import std::net::socket::TcpStream
import std::stdio::stdout

let listener = try! TcpListener
  .new(ip: '', port: 40_000)

let stream = try! TcpStream
  .new(ip: '', port: 40_000)

try! stream.write_string('hello')


let connection = try! listener.accept
let output = try! connection.read_string


stdout.print(output) # => 'hello'

Static typing

Inko is a statically-typed language, and supports generic types. The compiler comes with type inference, removing the need for manual type annotations in many places.

The use of static typing prevents a wide variety of bugs, and allows for many optimisations a dynamically-typed language may not be able to perform.


Inko is an object-oriented language. Unlike other object-oriented languages, Inko does not support inheritance. Instead, it relies on composition using traits.

The use of composition instead of inheritance prevents tight coupling between objects, without sacrificing flexibility.

Exceptions done right

Inko uses a form of checked exceptions, inspired by the article The Error Model by Joe Duffy. Exceptions must be handled where they can occur, and you can't throw exceptions from methods unless they are annotated accordingly.

Inko's error handling model takes the pain out of exception handling, without sacrificing performance or flexibility.

Learn more about error handling in the error handling guide.

Lightweight processes

For concurrency, Inko uses lightweight processes. These processes can't share memory and are garbage collected independently. Communication between processes is done by sending messages.

The lack of shared memory between processes means you don't have to worry about data races, or a garbage collector stopping your entire program.

Parallel garbage collection

Inko uses garbage collection to clean up memory, based on Immix. Each process is garbage collected independently, without the need for pausing the entire program.

To reduce garbage collection timings, the garbage collector performs its work using multiple threads.

Lightweight VM

The Inko VM is lightweight and fits on a single floppy disk (when stripped of any debugging symbols).

An empty Inko program uses about 5MB of RAM, and takes about 30 milliseconds to run.

Free Software

Inko is free and open source software, licensed under the Mozilla Public License 2.0.

The MPL 2.0 is not a viral license, meaning you can comfortably use Inko in proprietary projects.

Open and welcoming

The development of Inko happens in public, instead of behind closed doors. Donations are used exclusively to fund the development of Inko.

Everybody is welcome to contribute: whether it's fixing a bug, improving documentation, or simply telling your friends and colleagues about Inko.

Supports your platform

Inko supports Linux, Windows, and macOS. Inko should also work on BSDs such as FreeBSD and OpenBSD, but these platforms are not officially supported at this time. ARM is not supported at this time.

Inko requires a 64-bits architecture, and won't run on a 32-bits architecture.