Ki
Ki is a compiled, static, strongly-typed programming language that focuses on safety.
Safe
- Aggressive compile-time checks
- No undefined behavior
- No uninitialized variables
- No NULL pointers
- No pointers to uninitialized memory
- No out-of-range values (including checked [un]signed integer overflow)
- No aliasing
- Immutability by default
Powerful
- M:N parallelism support with fibers (coroutines)
- 1:1 parallelism support with threads
- Generators
- Strong, extendable type system
- Generics
- Traits
- Operator overloading
- C FFI library
Fast
- Compiled to machine code using LLVM
- Static (types are defined at compile-time)
- No built-in GC
Motivation
The major problem with software in our era is safety: values can be out of range, memory can be corrupted, control flow can be rerouted, execution can deadlock, etc. etc. etc.
The problem isn't programmers. Programmers are in general some of the most fastidious people there are. A single character out of place may render years of work into nothing but a bag of random bytes. A single math mistake may silently corrupt data or undermine cryptography. Many programmers are amazed that anything works at all.
The problem is that there are too many hazards and not enough tools for dealing with them. We have to use C or C++ in order to write the fastest software, and while the differences between the two are vast, what they share are myriad ways to invoke undefined behavior, dereference invalid pointers, write past the ends of our data structures, accept out-of-range values, and generally cause mayhem. Some prominent members of the tech community, and accomplished coders themselves, have publicly wondered whether or not it's even possible to write programs that don't have security bugs.
I don't know the answer. I do know we can make better tools so that, by giving up a very small amount of speed and capability, we can gain a wealth of robustness and expressive power.
Greetings
Ki is a very simple language to start out in. Here is "Hello World":
fn main() {
echo("Hello World!")
}
Built-In Functions
Ki provides four built-in functions.
print
prints formatted output to standard output. echo
is just like
print
, but it appends a newline to the output. fail
is just like echo
except it throws an error
to the outer scope. die
is also just like echo
except it exits the program after it finishes printing the message. There's no
coming back once you die, so you'd better be sure!
fn add(x: int, y: int): (int) {
return(x + y)
}
fn main() {
print("5 + 8 = {{add(5, 8)}}\n")
echo("5 + 8 = {{add(5, 8)}}")
die("5 + 8 = {{add(5, 8)}}... oh I'm much too tired now; I quit")
}
Importing Modules
Importing modules is very simple.
import math
fn sqrt_minus_one(x: int): (int) {
return(math.sqrt(x) - 1)
}
fn main() {
echo("Root 9 - 1 = {{sqrt_minus_one(9)}}. Super easy, right?")
}
Control Flow
Ki uses traditional control flow.
Conditionals
if
/else
should be very familiar.
fn main() {
name: "Charlie"
if (name == "Charlie") {
echo("Welcome back!")
}
else if (name == "Hillary Clinton") {
echo("Hello Madam!")
}
else {
echo("Hello, nice to meet you ${name}!")
}
}
Importantly, only boolean expressions are allowed. Other languages are more flexible: Python returns False for empty lists, None, and so on; C returns false for 0 and true for anything else, etc. Ki only handles boolean expressions in its conditionals.
Loops
Ki provides three loop types: for-in
, while
, and loop
.
This is a while
loop:
import math
fn print_until_seven() {
var x: math.rand()
while (x != 7) {
echo("{{x}}")
x = math.rand()
}
}
Ki has two other types of loops: for-in
and loop
.
fn print_vowels(input: string) {
for (r in input) {
if (r in "aeiouy") {
echo("Rune {{r}} is a vowel")
}
}
}
You can also enumerate what you're iterating over:
fn print_vowels(input: string) {
for (i, r in input) {
if (r in "aeiouy") {
echo("Rune {{r}} at index {{i}} is a vowel")
}
}
}
Use caution when indexing into strings. In Ki, string types are sequences of 32-bit Unicode codepoints encoded in UTF-8, and indexing into them yields 32-bit Unicode codepoints. Ki has a few types devoted to strings:
rune
: a 32-bit Unicode codepointstr
: Equivalent to C'sconst char[]
and stored in read-only process space (usually)string
: Equivalent to C'schar*
, read/write, but not resizabledynstring
: No true C equivalent, fundamentally achar*
, read/write, resizable
str
, string
, and dynstring
are encoded using UTF-8.
Note: iteration is more complicated than it appears here; see here for more information.
fn print_vowels(input: string) {
for (i, r in input) {
if (r in "aeiouy") {
echo("Rune {{r}} at index {{i}} is a vowel")
}
}
}
fn print_seven_times() {
loop (7) {
print("Hello!")
}
}
loop
takes an optional argument for how many times to iterate. If this is
omitted, it will loop forever.
You can also use break
to break out of these loops, and continue
to start
back at the top of the loop again.
With a little effort, you could achieve the functionality of any loop type with
any other loop type. Regardless, Ki provides these three in order to enhance
semantic clarity: for-in
should be used to iterate over a series of values;
while
should be used to iterate until a certain condition is met; loop
should be used to loop a certain number of times, including an infinite number
of times.