Skip to content

Instantly share code, notes, and snippets.

@osa1
Created June 28, 2025 10:20
Show Gist options
  • Save osa1/a5e7fdfa30d69125970c0797c525ede2 to your computer and use it in GitHub Desktop.
Save osa1/a5e7fdfa30d69125970c0797c525ede2 to your computer and use it in GitHub Desktop.
Koka example
// Code for the blog post: ...
// Tested with Koka 3.1.2.
import std/os/env
import std/os/file
import std/os/path
fun main()
val args = get-args()
val args-without-flags = args.remove(fn (arg) arg == "--test")
val run-test = args-without-flags.length != args.length
if run-test then
test()
else
run(args-without-flags)
fun run(args: list<string>): <exn, console, fsys>()
match args
Cons(pattern, files) ->
with fs-io
with console1-terminal
search(pattern, files)
Nil ->
println("USAGE: grep PATTERN FILE*")
fun search(pattern: string, files: list<string>): <fs, console1>()
val pattern-size = pattern.count()
files.foreach fn(file)
val contents = read-file(file.path)
val parts = contents.split(pattern)
report-matches(file, pattern-size, parts)
fun report-matches(file: string, pattern-size: int, parts: list<string>): <console1>()
if parts.length == 0 then
return ()
println1(file)
var line := 0
var column := 0
parts.init.foreach fn(part)
part.vector.foreach fn(char)
if char == '\n' then
line := line + 1
column := 0
else
column := column + 1
println1((line + 1).show ++ ":" ++ (column + 1).show)
effect fs
ctl read-file(path: path): string
effect console1
ctl println1(s: string): ()
val fs-io = handler
ctl read-file(path: path)
resume(read-text-file(path))
val console1-terminal = handler
ctl println1(s: string)
println(s)
resume(())
struct test-case
files: list<test-file>
pattern: string
expected-output: list<string>
struct test-file
path: path
contents: string
val test-cases: list<test-case> = [
Test-case(
files = [Test-file("file1".path, "test\ntest"), Test-file("file2".path, "a\n test\nb")],
pattern = "test",
expected-output = ["file1", "1:1", "2:1", "file2", "2:2"]
),
]
fun test(): <exn>()
var printed-lines := Nil
test-cases.foreach fn (test)
with handler
ctl read-file(path_: path)
match test.files.find(fn (file) file.path.string == path_.string)
Just(file) -> resume(file.contents)
Nothing -> throw("file not found", ExnAssert)
with handler
ctl println1(s: string)
printed-lines := Cons(s, printed-lines)
resume(())
search(test.pattern, test.files.map(fn (file) file.path.string))
if printed-lines.reverse != test.expected-output then
throw("unexpected test output", ExnAssert)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment