hica vs Koka
hica transpiles to Koka, so everything hica does ultimately is Koka. The question isn’t “which is more powerful?” (Koka will always win that). The question is: why add a layer on top?
The short answer: the differences are about intent, ergonomics, and tooling. Koka is a world-class research language exploring the cutting edge of algebraic effects. hica is an industrial-pragmatic language designed for building everyday software (CLI tools, parsers, utilities) with a familiar syntax, a curated feature set, and an integrated toolchain.
At a Glance
| Dimension | Koka | hica |
|---|---|---|
| Syntax style | Indentation-sensitive, ML-family | Braces + semicolons optional, C/Rust-family |
| Type annotations | Required on public functions | Optional everywhere (Hindley-Milner infers) |
| Effects | Explicit row-typed effect system (user declares and handles) | Built-in effects inferred implicitly + user-defined effect / handle / spawn |
| Pattern matching | Deep, exhaustive, with handlers | Common cases: primitives, Maybe/Result, structs, ranges, lists |
| Custom effects | First-class: effect, handler, resume |
First-class: effect, handle, stateful handlers (with var …), effect-row polymorphism, actor sugar, and named effects (spawn Name … as ref + ref.op(args) + ref<Name>) |
| Data types | type, struct, alias, effect with full generics |
struct + type enums, inferred polymorphism |
| String interpolation | println("x = " ++ x.show) |
println("x = {x}") |
| String concatenation | ++ |
+ |
| Pipe operator | Not built-in (use dot-call) | \|> built-in + dot-call |
| List syntax | Cons(1, Cons(2, Nil)) or [1,2,3] |
[1, 2, 3] always, [h, ..rest] patterns |
| Map literals | No built-in syntax | {"key": value} literals |
| Loops | while, for, repeat via effects |
while, for, repeat, loop, break, continue |
| Mutability | Local var via effect handlers |
var with familiar reassignment syntax |
| Module system | File-based, pub visibility |
import / from M import { f } / pub import |
| Learning curve | Steep (effects, handlers, row types) | Gentle (write, run, iterate) |
| Target audience | PL researchers, effect-system enthusiasts | Beginners, educators, application programmers |
Syntax: Familiar vs Novel
Koka’s syntax is ML-inspired. It is clean but unfamiliar to most programmers:
fun factorial(n : int) : int
if n <= 1 then 1
else n * factorial(n - 1)
fun main()
println(factorial(5))
hica uses braces, => arrows, and let, patterns that developers recognise from Rust, TypeScript, and C#:
fun factorial(n) => if n <= 1 { 1 } else { n * factorial(n - 1) }
fun main() {
println(factorial(5))
}
Neither is objectively better. But if you are teaching a class, onboarding junior developers, or writing your first compiled language, the brace style has less to explain.
Effects: Curated Complexity
This is the biggest philosophical difference. Koka’s effect system is its defining feature, and its steepest learning curve.
In Koka, every function signature tells you what effects it uses. You must understand effect rows, effect polymorphism, and how handlers work before you can write non-trivial programs:
fun greet() : console ()
println("hello")
fun read-config() : <fsys, exn> string
read-text-file("config.txt".path)
hica curates this power: you get the safety benefits of side-effect tracking (visible via hica check) without writing effect annotations yourself for built-in effects:
fun greet() {
println("hello")
}
fun read_config() => read_file("config.txt")
The hica compiler infers and emits the correct effects in the generated Koka code, so you get Koka’s safety guarantees without the annotation burden for everyday code.
When you do want first-class control, hica exposes user-defined algebraic effects with a friendlier surface than raw Koka: effect Name { fun op(...) : R } declarations, handle Name { arm, ... } (with var ...)? in { body } handlers (state included), effect-row polymorphic function types (() -> <E> R) for capability sandboxes, an actor Name { ... } sugar for message-driven components, and named effects — spawn Name { arms } as ref plus per-instance ref.op(args) dispatch with a first-class ref<Name> type — for running multiple independent handler instances in one function. See docs/effects for the full guide.
You lose Koka’s fine-grained row-polymorphism annotations on ordinary functions (rows are inferred, not written), non-resuming control effects, and multi-shot resumption. If you need those, you are ready for Koka.
Type Annotations: Required vs Optional
Koka requires type annotations on public function signatures:
pub fun add(a : int, b : int) : int
a + b
hica infers types across function boundaries. Annotations are always optional:
pub fun add(a, b) => a + b
You can add them when you want documentation:
pub fun add(a: int, b: int) : int => a + b
This lowers the barrier for beginners. You do not need to understand the type system to write correct programs; the compiler figures it out.
String Handling
Koka uses ++ for concatenation and show for conversion:
fun main()
val name = "world"
println("Hello, " ++ name ++ "! " ++ show(42))
hica uses + and has built-in string interpolation:
fun main() {
let name = "world"
println("Hello, {name}! {42}")
}
Small difference, big impact on readability when building messages and output.
Data Structures
Lists
Koka’s list operations require understanding of effect signatures and sometimes explicit types:
fun main()
val nums = [1, 2, 3, 4, 5]
val evens = nums.filter(fn(x) x % 2 == 0)
val doubled = evens.map(fn(x) x * 10)
println(doubled.show)
hica has the same operations with lighter syntax and a pipe operator:
fun main() {
let result = [1, 2, 3, 4, 5]
|> filter((x) => x % 2 == 0)
|> map((x) => x * 10)
println(result)
}
Maps
Koka has no built-in map literal syntax. You would use std/data/dict or build association lists manually.
hica has map literals:
fun main() {
let ages = {"kalle": 30, "olle": 25}
println(ages.map_get("kalle")) // Just(30)
}
Struct update syntax
Koka:
val p2 = p(x = 10)
hica:
let p2 = Point { ...p, x: 10 }
The hica spread syntax is more explicit about what is happening.
Pattern Matching
Both languages have exhaustive pattern matching. Koka’s is more powerful (nested destructuring, guards on handlers, effect matching), whilst hica covers the common cases with a syntax closer to Rust:
fun describe(x) => match x {
0 => "zero",
1 | 2 => "few",
3..=10 => "several",
_ => "many"
}
fun sum(xs: list<int>) : int => match xs {
[] => 0,
[x, ..rest] => x + sum(rest)
}
hica also has bit-level pattern matching with ? wildcards, something Koka does not have:
match opcode {
0b11??_???? => "category 3",
0b10??_???? => "category 2",
_ => "other"
}
Loops and Control Flow
Koka models loops through effect handlers. while and for exist but break and continue require understanding effect-based control flow:
fun main()
for(1, 10) fn(i)
println(show(i))
hica has imperative-style loops with break and continue that work as you would expect:
fun main() {
for i in 1..10 {
if i % 3 == 0 { continue }
println(i)
}
var count = 0
while count < 5 {
println(count)
count = count + 1
}
loop {
if count > 10 { break }
count = count + 1
}
}
Modules and Imports
Koka uses qualified imports:
import std/os/path
import std/os/file
hica uses string-based imports with selective imports and re-exports:
import "std/os/path"
from "std/os/file" import { read_file, write_file }
pub import "mylib/utils"
Error Handling
Koka uses effects for exceptions, which is powerful but requires understanding effect handlers:
fun safe-divide(a : int, b : int) : exn int
if b == 0 then throw("division by zero")
a / b
hica uses Result and Maybe types with combinators, the same approach as Rust:
fun safe_divide(a, b) =>
if b == 0 { Err("division by zero") }
else { Ok(a / b) }
fun main() {
let result = safe_divide(10, 2)
|> map_result((n) => n * 10)
|> and_then_result((n) => safe_divide(n, 5))
println(result)
}
Integrated Toolchain
Koka provides a compiler. hica provides a workspace.
| Command | What it does |
|---|---|
hica run file.hc |
Compile and run in one step |
hica test file.hc |
Run test blocks with built-in assertions |
hica check file.hc |
Analyse and report errors without building |
hica fmt file.hc |
Format source code |
hica repl |
Interactive read-eval-print loop |
hica new myproject |
Scaffold a new project |
hica build file.hc -o bin |
Build a standalone binary |
hica clean <file> |
Remove generated artifacts for a file |
hica clean --cache |
Remove the stdlib cache |
The test runner is built on kunit, and the CLI argument parser on klap. Both are purpose-built Koka libraries that ship with hica. You do not install a test framework or argument parser separately; they are part of the toolchain.
Koka has koka -e file.kk to compile-and-run and koka -o to build, but testing, formatting, project scaffolding, and REPL are not built into the compiler workflow.
HML: A Companion Configuration Language
hica has a purpose-built configuration library: HML (Hica Markup Language). HML is a structured data format with first-class support for types like durations, date-times (RFC 3339), and tagged elements, primitives that most config languages encode as strings.
@server(port: 8080, public) {
host: "localhost"
timeout: 30s
started: 2026-05-20T10:00:00Z
}
Add it with the package manager and import:
hica add hml
import "hml"
Koka has no companion config format; you would reach for JSON or YAML and write your own parsing layer.
When to Use Which
Use hica when:
- You are learning your first compiled language
- You are teaching programming to students
- You want type safety and immutability without a steep learning curve
- You want user-defined algebraic effects (
effect,handle,spawn,actor) with a friendly surface and none of the row-annotation ceremony - You want a familiar, curly-brace syntax with automatic memory management
Use Koka when:
- You need non-resuming control effects or multi-shot resumption (hica ships single-shot, resuming effects only)
- You are doing research in programming language theory
- You need to write explicit row-polymorphic annotations on ordinary functions
- You have outgrown hica’s feature set and want the full power underneath
The Bridge
Because hica compiles to Koka, the transition is smooth:
- Import Koka modules from hica. hica can import
.kkfiles directly. Write a library in Koka, use it from hica. - Read the generated code. Run
hica build <file>.hcto see the Koka output. As you learn, you will start recognising the patterns. - Graduate when ready. The concepts transfer:
match, immutability,Result/Maybe, function composition are the same in both languages. Koka adds effects and handlers on top.
hica is not a replacement for Koka. It is a front door, a way in that does not require understanding row-typed effects before you can write hello world.
Conclusion
Koka is a brilliant language with ideas that are ahead of their time. Its effect system is genuinely novel and powerful. But that power comes with a learning curve that puts it out of reach for many programmers.
hica keeps the parts that make Koka great (Perceus memory management, algebraic data types, expression-oriented design, compiled performance, and user-defined algebraic effects) and wraps them in a syntax and programming model that is immediately accessible. You lose row-polymorphic annotations on ordinary functions, non-resuming control, and multi-shot resumption. You gain an integrated toolchain, a companion config language, and a language you can teach in an afternoon.
In short: Koka provides the powerful underlying engine; hica provides a specific, ergonomic cockpit designed for building software tools quickly and safely.