KonLang English

Học ngôn ngữ

Đây là Kon đúng như compiler đang chạy. Mọi chương trình đầy đủ ở đây đều đã được compiler đó build và chạy, và output bên dưới là kết quả thật.

Xin chào

func main(): i32 {
    print("xin chào")
    return 0
}
xin chào

main trả về mã thoát của chương trình. Hết dòng là hết câu lệnh, nên không cần dấu chấm phẩy. Chạy bằng kon run hello.kon.

Giá trị

let chỉ gán một lần, còn var gán lại được. Kiểu thường được tự suy ra, và bạn chỉ cần ghi kiểu khi một con số có thể mang nhiều kiểu.

func main(): i32 {
    let name = "Kon"
    var count: i32 = 1
    count += 41
    let ratio: f64 = 1.5
    let small = count as u8
    print(name)
    print(count)
    print(ratio)
    print(small)
    return 0
}
Kon 42 1.5 42

Các kiểu số gồm i8 đến i64, u8 đến u64, f32 và f64. Đổi kiểu luôn phải ghi bằng as. Số nguyên khi tràn sẽ quay vòng, giống nhau trên mọi máy, còn chia cho 0 sẽ dừng chương trình. Số thực đổi sang số nguyên sẽ bị chặn ở giới hạn của kiểu.

Hàm

const LIMIT: i32 = 100

func clamp(value: i32): i32 {
    if value > LIMIT { return LIMIT }
    return value
}

func main(): i32 {
    print(clamp(250))
    return 0
}
100

Rẽ nhánh và lặp

func main(): i32 {
    var total: i64 = 0
    for i in 0..(5 as i64) {
        if i == 3 { continue }
        total += i
    }
    var left: i32 = 3
    while left > 0 {
        left -= 1
    }
    print(total)
    print(left)
    return 0
}
7 0

Struct và method

Method nằm ngay trong struct. Tham số đầu tiên cho biết method làm gì với struct: view self để đọc, edit self để sửa.

struct Counter {
    value: i32

    func get(view self): i32 {
        return self.value
    }

    func bump(edit self, by: i32) {
        self.value += by
    }
}

func main(): i32 {
    var counter = Counter { value: 40 }
    counter.bump(2)
    print(counter.get())
    return 0
}
42

Enum và match

Mỗi trường hợp của enum có thể mang dữ liệu, và match phải xử lý đủ mọi trường hợp.

enum Shape {
    Circle(f64)
    Square(f64)
    Empty
}

func area(shape: view Shape): f64 {
    match shape {
        Shape.Circle(radius) => { return 3.14 * radius * radius }
        Shape.Square(side) => { return side * side }
        Shape.Empty => { return 0.0 }
    }
    return 0.0
}

func main(): i32 {
    let square = Shape.Square(3.0)
    print(area(square))
    return 0
}
9

Option và Result

Kon không có null. Giá trị có thể thiếu là Option, và try trả giá trị thiếu hoặc lỗi lên cho hàm gọi.

func half(value: i32): Option<i32> {
    if value % 2 != 0 { return none }
    return some(value / 2)
}

func quarter(value: i32): Option<i32> {
    let once = try half(value)
    return half(once)
}

func main(): i32 {
    match quarter(20) {
        some(result) => { print(result) }
        none => { print("not whole") }
    }
    let missing = quarter(6)
    print(missing.isSome())
    return 0
}
5 false

List và mảng

List có thể lớn dần trên heap, còn mảng cố định thì không. Mọi chỉ số đều được kiểm tra: vượt quá cuối sẽ dừng chương trình chứ không đọc nhầm vùng nhớ khác.

func main(): i32 {
    var scores: List<i32> = list[]
    scores.add(7)
    scores.add(35)
    var total: i32 = 0
    var at: u64 = 0
    while at < scores.length {
        total += scores[at]
        at += 1
    }
    print(total)

    let fixed: [u8; 3] = [1, 2, 3]
    print(fixed[2 as u64])
    return 0
}
42 3

Chữ

str là chữ chỉ để xem, còn String là chữ bạn sở hữu và có thể nối thêm. Cả hai đều là UTF-8. Hỏi "độ dài" là lỗi có chủ đích, vì số byte, số ký tự và số chữ nhìn thấy là ba con số khác nhau.

func main(): i32 {
    var greeting = String("xin ")
    greeting.append("chào")
    print(greeting)
    print(greeting.byteLength)
    print(greeting.characterCount())
    return 0
}
xin chào 9 8

Đổi số, tách chữ, tìm kiếm và đọc file thì xem std/text.

Ownership

Mỗi giá trị có đúng một chủ, và chủ sẽ giải phóng nó, không cần garbage collector. Khi truyền một giá trị, lời gọi hàm cho thấy chuyện gì xảy ra với nó:

Bạn viếtÝ nghĩaSau đó
show(player)Hàm chỉ đọc; view không cần ghi ở lời gọiBạn vẫn là chủ
heal(edit player, 5)Hàm sửa nó, và trong lúc đó không gì khác được đụng vàoBạn vẫn là chủ
keep(take player)Bạn giao nó điBạn không dùng được nữa

Dùng một giá trị sau khi đã giao đi sẽ không biên dịch được, và lỗi chỉ rõ dòng:

func main(): i32 {
    var items: List<u8> = list[]
    let other = take items
    print(items.length)
    return 0
}
K2-OWN-001: value was moved or is not initialized at game.kon:4:11

Sửa một giá trị trong lúc có chỗ khác đang đọc nó cũng vậy:

func copyInto(target: edit List<u8>, source: view List<u8>) {
}

func main(): i32 {
    var items: List<u8> = list[]
    copyInto(edit items, items)
    return 0
}
K2-BORROW-001: access conflicts with a live borrow at game.kon:6:26

Việc chuyển một giá trị có tên phải được nhìn thấy, nên quên take cũng là lỗi:

func keep(items: List<u8>) {
}

func main(): i32 {
    var items: List<u8> = list[]
    keep(items)
    return 0
}
K2-OWN-002: named owning value requires explicit take at game.kon:6:10

Generic và interface

interface Named {
    func label(view self): i32
}

struct Cat implements Named {
    lives: i32

    func label(view self): i32 {
        return self.lives
    }
}

func labelOf<T>(item: view T): i32 where T: Named {
    return item.label()
}

func main(): i32 {
    let cat = Cat { lives: 9 }
    print(labelOf<Cat>(cat))
    return 0
}
9

Module

Mỗi file là một module, và chỉ những tên có pub mới nhìn thấy được từ bên ngoài. Import tìm cạnh file nguồn trước, sau đó các đường dẫn std/ mới tìm trong thư viện đi kèm toolchain.

import "std/text.kon" as text

func main(): i32 {
    let number = text.fromInt(42)
    print(number)
    return 0
}
42

Dọn dẹp bằng defer

defer chạy khi khối lệnh kết thúc, theo thứ tự ngược lại, dù khối kết thúc theo cách nào.

func main(): i32 {
    defer {
        print("last")
    }
    defer {
        print("second")
    }
    print("first")
    return 0
}
first second last

Gọi C

Gọi hàm ngoài là chỗ duy nhất compiler không kiểm tra được, nên phải đặt trong unsafe để người đọc dễ tìm thấy.

extern "C" func abs(value: i32): i32

func main(): i32 {
    var result: i32 = 0
    unsafe {
        result = abs(-42)
    }
    print(result)
    return 0
}
42

Chưa có trong ngôn ngữ

Spec đi xa hơn những gì compiler làm được hiện tại. Những thứ sau chưa chạy: closure, nội suy chuỗi như "n = {n}", enum chứa List của chính nó, và tự đổi giữa str với String. Trang trạng thái có đầy đủ hơn.