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 {
  let message = process.receive as String

  # This will print "Hello" to STDOUT.
  stdout.print(message)
}

proc.send('Hello')
object List(T) {
  @values: Array!(T)

  def init {
    @values = Array.new
  }

  def push(value: T) -> T {
    @values.push(value)
  }
}

let list = List.new

# This is OK:
list.push(10)

# This will produce a type error:
list.push('oops')
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) {
  stderr.print(error.message)
}

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

# This will open a file in read-only
# mode. The use of "try!" causes the
# program to abort (= a panic) if the
# file could not be opened.
let handle = try! file.read_only('text.txt')
let content = try! handle.read_string

stdout.print(content)
import std::fs::file

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

# This will produce a type error, since
# the file is opened in read-only mode.
readme.write_string('oops')

# This also won't work, because we can
# not remove a read-only file.
readme.remove
import std::test
import std::test::assert

test.group('Integer.+') do (group) {
  group.test('Summing two Integers') {
    assert.equal(1 + 2, 3)
  }
}

test.run
import std::conversion::ToString
import std::stdio::stdout

object Person {
  @name: String

  def init(name: String) {
    @name = name
  }
}

impl ToString for Person {
  def to_string -> String {
    @name
  }
}

let person = Person.new('Alice')

# This will print "Alice" to STDOUT:
stdout.print(person)
# ?User means we may return a User, or Nil.
def find(email: String) -> ?User { }

def update(user: User) { }

let user = find('alice@example.com')

# This will return the username if a User
# was returned, or Nil if "user" is Nil:
user.username

# This will produce a type error, because
# User doesn't respond to "oops":
user.oops

# This won't work, because "update" takes a User, and not a ?User.
update(user)

user.if_true {
  # `user!` tells the compiler to treat "user" as a User, not a ?User.
  update(user!)
}
# Inko supports tail call elimination, so this
# method will not overflow the call stack.
def fact(num: Integer, acc = 1) -> Integer {
  num.zero?.if_true {
    # This will return from the method, not
    # the surrounding closure.
    return acc
  }

  fact(num: num - 1, acc: acc * num)
}

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

let mut number = 0

{ number < 10 }.while_true {
  stdout.print('This loop will run 10 times')

  number += 1
}

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

let numbers = Array.new(10, 20, 30)

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

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

new_numbers # => Array.new(20, 40, 60)

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.

Object-oriented

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.

Sponsors