# Gotlin Gotlin is a Kotlin-flavored language implemented in Go. It compiles typed Gotlin source to Go and uses the Go toolchain for binaries, packages, and interop. ```kotlin package main fun fibonacci(value: Int): Int { if (value < 2) { return value } return fibonacci(value - 1) + fibonacci(value - 2) } fun main() { println(fibonacci(10)) } ``` ## Compiler pipeline ```text Lexer -> parser and syntax AST -> package resolver and structural TypeRef resolution -> lexical symbols, type checking, effects, and diagnostics -> typed HIR -> Go emission -> Go compiler ``` Semantic types distinguish named values, Gotlin classes, nullable values, Go pointers, functions, generics, tuples, and imported Gotlin classes. A Gotlin class remains `User` throughout semantic analysis; only Type-to-Go lowering chooses the `*User` representation. Separately compiled Gotlin packages expose a versioned `.gti.json` interface containing classes, enums, signatures, generics, and inferred effects. The compiler and LSP load package interfaces before using `go/types` for ordinary Go dependencies. ## Functions and values ```kotlin fun greet(name: String): String { val message = "Hello " + name return message } fun doubled(value: Int) = value * 2 ``` `val` is immutable and `var` is mutable: ```kotlin val accountId = "account-1" var attempts = 0 attempts += 1 ``` Supported control flow includes `if`, `else`, `while`, `for`, `try`, `catch`, `throw`, exhaustive `match`, and `defer`. ## Classes and interfaces Gotlin classes are reference-valued by default: ```kotlin interface Greeter { fun greet(name: String): String } class PrefixGreeter(val prefix: String): Greeter { fun greet(name: String) = prefix + name } fun create(): PrefixGreeter = PrefixGreeter("Hello ") ``` Use `*` only for Go pointer types: ```kotlin fun handle(request: *http.Request, pool: *pgxpool.Pool) { } ``` Imported Gotlin classes retain reference semantics across package boundaries: ```kotlin fun publish(lifecycle: platform.Lifecycle) { } ``` ## Null safety Types are non-nullable unless marked with `?`: ```kotlin fun email(user: User?): String { if (user == null) { return "missing" } return user.email } ``` Safe access and non-null assertions are available: ```kotlin val city: String? = user?.address?.city val required: User = optionalUser!! ``` The semantic analyzer smart-casts values after null checks and guard clauses. ## Data classes and JSON ```kotlin @jsonNaming(camelCase) data class AccountReply( var accountId: String, var availableBalance: Double ) ``` Supported JSON policies are `snakeCase`, `camelCase`, `pascalCase`, and `kebabCase`. ```kotlin val body = json.marshal(reply).unwrap() val decoded = json.decode(body).unwrap() ``` Resources can be embedded at package scope: ```kotlin import embed @embed("static/*") val assets: embed.FS ``` ## Enums and match Enums support payloadless and payload variants: ```kotlin enum PaymentResult { Accepted(String) Rejected(String) Pending } ``` Matches are exhaustive and may return values: ```kotlin fun description(result: PaymentResult) = match (result) { PaymentResult.Accepted(id) -> "accepted " + id PaymentResult.Rejected(reason) -> "rejected " + reason PaymentResult.Pending -> "pending" } ``` Payloadless enums are represented as exact string-backed values, making them suitable for JSON and PostgreSQL columns. ## Result error handling ```kotlin fun parse(value: String): Result = strconv.atoi(value) fun doubled(value: String): Result { val parsed = strconv.atoi(value)? return Result.Ok(parsed * 2) } ``` Available operations include `?`, `unwrap()`, `unwrapOr(value)`, explicit destructuring, and exhaustive `Result` matching. Go `(T, error)` and error-only returns adapt to `Result` when required by context. ## Generics and lambdas ```kotlin data class Box(var value: T) { fun get(): T = value } fun identity(value: T): T = value val inferred = identity(42) val explicit = identity("value") val boxed = Box("text") ``` Higher-order functions use Kotlin-style function types and trailing lambdas: ```kotlin fun transform(value: T, block: (T) -> R): R = block(value) val answer = transform(21) { value -> value * 2 } ``` The final expression is returned automatically for value lambdas. Unit lambdas execute their final expression as a statement. Explicit `return` is also supported. ## Structured concurrency and context Coroutine effects are inferred from direct and transitive calls: ```kotlin fun poll() { while (isActive()) { receiveEvents() delay(1000) } } fun main() { runBlocking { launch { poll() } } } ``` `launch` creates a structured Unit child. `async` creates a typed deferred child. Scopes wait for children, propagate failures, and cancel siblings. Available operations include `runBlocking`, `withContext`, `coroutineScope`, `launch`, `async`, `await`, `delay`, `withTimeout`, `isActive`, and `coroutineContext`. HTTP request contexts can establish an ambient scope with an expression-bodied function: ```kotlin fun handle(request: *http.Request) = withContext(request.context()) { service.processRequest() } ``` Go calls whose first parameter is `context.Context` receive the ambient context automatically when that argument is omitted: ```kotlin fun command(): *exec.Cmd = exec.commandContext("date") ``` Passing an explicit context suppresses automatic injection. ## Collections and channels ```kotlin val names = listOf("Ada", "Linus") val scores = mapOf("Ada", 10, "Linus", 8) val channel = Channel(1) channel.send("ready") val message = channel.read() ``` Collection types include `List`, `MutableList`, `Map`, and `MutableMap`. ## Structural mapping `mapTo` maps compatible classes, enums, collections, and nullable values: ```kotlin fun response(account: AccountEntity): AccountResponse = account.mapTo() val response = account.mapTo() ``` Fields are matched by name and mappings are validated recursively with path-specific compile errors. ## Typed PostgreSQL DSL SQL rows are data classes with table metadata: ```kotlin @table("accounts") data class AccountRow( @generated @id var id: String, var customerId: String, var balance: Double, var closedAt: time.Time? ) ``` ### Queries, aliases, joins, groups, and aggregates ```kotlin @table("customers") data class CustomerRow(@id var id: String, var name: String) data class AccountSummary( var customerId: String, var customerName: String, var total: Double, var entries: Long ) fun summaries(minimum: Double): GotlinSQLQuery = sql.from() .alias("account") .leftJoin("customer") { account, customer -> account.customerId == customer.id } .select { account, customer -> AccountSummary( account.customerId, customer.name, sum(account.balance), count() ) } .where { account, customer -> account.balance > minimum } .groupBy { account, customer -> listOf(account.customerId, customer.name) } .having { account, customer -> sum(account.balance) > minimum } .orderByDescending { account, customer -> sum(account.balance) } .limit(100) .offset(20) .build() ``` Join methods include `join`, `leftJoin`, and `rightJoin`. Aggregates include `count`, `countDistinct`, `sum`, `avg`, `min`, and `max`. ### Inserts and bulk inserts ```kotlin val insert = sql.insert(row).build() val bulk = sql.insertAll(rows).build() ``` Bulk inserts create runtime-sized PostgreSQL placeholder lists and reject empty input. ### Conflicts and updates ```kotlin val upsert = sql.insert(row) .onConflict { it.id } .doUpdate { excluded -> AccountRow.balance = excluded.balance } .build() val update = sql.update() .set { row -> row.balance = row.balance + amount row.closedAt = now() } .where { it.id == accountId } .returning { it } .build() ``` Write targets are checked for existence, mutability, nullability, duplicate assignment, and value compatibility. ### Execution ```kotlin val accounts = sql.from() .where { it.customerId == customerId } .fetch(pool, ctx) .unwrap() val account = sql.from() .where { it.id == accountId } .single(pool, ctx) .unwrap() ``` Execution terminals are `fetch`, `single`, and `iterator`. They return typed `Result` values and use generated row scanners. ## Go interop Go packages are imported directly: ```kotlin import http net.http import pgxpool "github.com/jackc/pgx/v5/pgxpool" ``` Go selectors are written in lower camel case and emitted with exported Go names. Named arguments construct external Go structs: ```kotlin val client = http.Client(timeout = 3 * time.second) ``` `go/types` and `go/packages` provide function, method, field, alias, tuple, variadic, and error-return signatures to semantic analysis and the LSP. ## Build and run ```bash go run ./cmd/gotlinc build ./examples/hello.gt ./hello go run ./cmd/gotlinc run ./examples/hello.gt ``` Emit Go source: ```bash go run ./cmd/gotlinc build -src ./examples/hello.gt -o /tmp/hello.go ``` Emit and consume package interfaces: ```bash gotlinc build -src \ -metadata-output platform.gti.json \ -metadata-package example/platform \ platform.gt -o platform.go gotlinc build -src \ -metadata platform.gti.json \ service.gt -o service.go ``` ## Language server and editor Build the language server: ```bash go build -o ./bin/gotlin-lsp ./cmd/gotlin-lsp ``` The VS Code extension under `tools/vscode-gotlin` provides syntax highlighting, snippets, diagnostics, hover, document symbols, definitions, references, and a `gopls` bridge for imported Go APIs. ```bash cd tools/vscode-gotlin npm install npm run check ```