The Co programming language
Go, TypeScript, Koka, Kotlin, Clojure and WebAssembly walks into a bar…
Programs are divided into packages than can be imported.
import "foo"
import bar "bar"
import . "lol/cat"
import (
"bob-hello"
bar2 "alt/bar"
. "meow"
)
Functions are defined with the fun
keyword.
// simple, empty functions
fun empty0
fun empty1()
fun empty2(
// no implicit semicolons inserted here
)
fun empty3() {}
// parameters
fun fp2(int, int) int // types only
fun fp1(a, b, c int) int // shorthand type
fun fp3(a, b, c int) int { 0 } // with body
fun fp4(a, b int, d, e f32) int { 0 } // shorthand types
// fun fp5(a int, int)int //error: mixed named and unnamed function parameters
// fun fp6(pkg.T int) {} // error: illegal parameter name
// trailing commas in params
// fun fp7(,) // error: unexpected ,, expecting name or type
// fun fp9(;) // error: unexpected ;, expecting name or type
fun fp10(int,)
fun fp11(a, b int,)
// rest
fun rest1(a ...int) {}
fun rest2(a, b ...int) {} // == (a int, b ...int)
// fun rest3(a ...int, b ...int) {} // error: can only use ... with final param
// fun rest4(a ...) {} // error: unexpected ), expecting type after ...
// result
fun fr0() int {}
fun fr1() (int) {}
fun fr2() (int,) {}
fun fr3() () {}
fun fr4() (i32, i64) {}
// fun fr5() (a t0) {} // error: unexpected name, expecting comma, or )
// single-expression body
fun fx0(a int) int -> 4
fun fx1(a int) -> (4, 4)
fun fx2() -> 4
fun fx3 -> 4
// fun fx4(b) int -> ; // error: missing function body
// fun fx5(b) -> ; // error: missing function body
// fun fx6 -> ; // error: missing function body
// fun fx7 -> return 3 // error: unexpected return, expecting expression
// used as an expression
fe0 = fun foo0(int) { 4 }
fe1 = fun foo1(int) -> 4
fe2 = fun (int) { 4 } // name is optional when used as an expression
fe3 = fun (int) -> 4
fe4 = fun -> 4
// fe5 = fun (d) int // error: missing function body
// funfunfun
fun a() ->
fun b() ->
fun c() -> 123
There are tuples, too, with compile-time indexing.
fun tuple1 {
xs = (1, 2.3, true, "3") // type: (i32, f64, bool, str<1>)
// constant-expression field access (all these accesses field 1 <f64>)
_ = xs[1]
_ = xs[+1]
a = -1 ; _ = xs[-a]
b = -2 ; _ = xs[^b]
c u32 = 0xfffffffe ; _ = xs[^c]
d u64 = 0xfffffffffffffffe ; _ = xs[^d]
e i64 = -2 ; _ = xs[^e]
f i64 = 3 ; _ = xs[f + b]
g = 3 ; _ = xs[g + b]
_ = xs[g >> 1]
h1 = 1 ; h2 = h1 + 1 ; h3 = h2 - 1 ; h4 = h3 ; _ = xs[h4]
// literal field access
_ = xs.0 // int
_ = xs.1 // f64
_ = xs.2 // bool
_ = xs.3 // str<1>
// // _ = xs.4 // error: out-of-bounds tuple index 4 on type (int, ...)
// slicing tuples
_ = xs[1:3] // (f64, bool)
_ = xs[1:] // (f64, bool, str<1>)
_ = xs[:3] // (i32, f64, bool)
_ = xs[:] // (i32, f64, bool, str<1>)
// _ = xs[1:2] // invalid single-element slice of type ...
// _ = xs[2:2] // invalid empty slice: 2 == 2
// _ = xs[2:1] // error: invalid slice index: 2 > 1
// _ = xs[4:] // invalid for tuples; would yield empty tuple
// error: non-constant tuple index
// k = 0
// if k == 0 { k = k + 1 } // makes k variable
// e0 = xs[k] // error: non-constant tuple index
}
fun late1 {
// late resolution
xs2 = (1, latestr) // resolved to (int, str<5>) in post-resolve
v = xs2.1 // resolved to str<5> in post-resolve
}
latestr = "hello"
Templates (generics):
fun generics {
type T1 (int, f32, List<int>)
type S1<A,B> { a A; b B } // Structure template with two template variables
type S2 S1 // Distinct alias of S1
_ u32[]
_ u32[][]
_ S1<i32,S1<u32,f32>> // (i32, (u32, f32))
_ S1<i32,S1<i32,S1<u32,f32>>> // (i32, (i32, (u32, f32)))
_ S1<int,str>[] // S1<int,str>[]
_ List<S1<int,str>>[] // S1<int,str>[][]
_ List<S1<int,str>[]> // S1<int,str>[][]
_ List<S1<int,str>[]>[] // S1<int,str>[][][]
_ List<int> // int[]
_ List<int> // int[]
_ List<f32> // f32[]
_ List<f32> // f32[]
_ T1.2 // int[] because T1.2 = List<int>
_ T1.2[] // int[][] because T1.2 = List<int>
// ambiguous expressions
a, b, x = 1, 2, 3
_ = x < a && b > a // (assign _ (ANDAND (LSS x a) (GTR b a)))
_ = S2<int,f32>() // (assign _ (call (type S2<int,f32>) ()))
_ = unkn<int,f32>() // (assign _ (call (type ~unkn<int,f32>) ()))
_ = List<int>() // (assign _ (call (type int[]) ()))
_ int[] = List<int>() // (var _ (call (type int[]) ()))
_, _ = a<b,a>b // (assign _ (LSS a b) (GTR a b))
_ = [a<b,a>b] // (assign _ (List (LSS a b) (GTR a b)))
_ = [S1<int,f32>] // (assign _ (List (type S1<int,f32>)))
S1<int,f32>() // (call (type S1<int,f32>)) ())
List<int>() // (call (type int[]) ())
}
// late-resolved type alias
type unkn List