Skip to content
skotch
...

Language Overview

Skotch implements a growing subset of the Kotlin language. This page summarizes what works today and what is coming next.

FeatureSpec referenceNotes
Function declarations§4.1Top-level fun, parameters, return types
Default parameters§4.1.1fun f(x: Int = 10) — literal defaults injected at call sites
Named arguments§7.2.2f(height = 3, width = 4) — arguments reordered to match parameter positions
Expression body functions§4.1fun f() = expr shorthand
Extension functions§4.1.3fun Int.isEven(), this receiver, method chaining
Local functions§4.1.4fun inside blocks, recursive calls
Data classes§4.5.6data class Point(val x: Int, val y: Int) — synthesized toString() producing "Point(x=1, y=2)"
Class declarations§4.5Primary constructor with val/var, field access, instance methods, init blocks, invokevirtual dispatch
Mutable class fields§4.2var fields in classes with method writeback; count++ in methods persists
Override toString()§4.5.1override fun toString(): String on regular classes
Increment/decrement§7.3.4x++, x-- — postfix operators on var locals and class fields
Object declarations§4.5.4object Singleton { fun greet() {} } — methods as static functions; Singleton.greet() calls
Enum classes§4.5.7enum class Color { RED, GREEN, BLUE } — entries accessed via Color.RED, usable in when
Companion objects§4.5.4companion object { fun create() } — static methods via ClassName.method()
Const val§4.2const val compile-time constants inlined at call site
Visibility modifiers§4.3private, internal, protected, open, abstract — parsed and accepted (not yet enforced)
Java interop§18Real .class parsing from JDK jmods + CLASSPATH; deferred resolution; clear classpath errors
Import declarations§9.2import java.lang.Math, implicit java.lang.*
Variable declarations§4.2val (immutable), var (mutable), type annotations
Integer literals§7.1.1Decimal, hex (0xFF), binary (0b1010), underscores (1_000), L suffix
Character literals§7.1.5'A', escape sequences ('\n', '\t', '\\')
Boolean literals§7.1.3true, false
String literals§7.1.4Regular, raw ("""), templates ($x, ${expr})
Long literals§7.1.1100L, 0xFFL — full 64-bit arithmetic (+, -, *, /, %), negation, values exceeding Int range
Double/Float literals§7.1.23.14, 2.5e10, 1.0f, negative doubles, scientific notation
Null literal§7.1.6null value, println(null)
Elvis operator§7.4.3x ?: default — null-check with fallback, supports chaining
Nullable parameters§3.3fun f(x: String?): String, nullable function parameters
Arithmetic operators§7.5+, -, *, /, % on Int, Long, and Double
String concatenation§7.5String + String, String + Int/Long/Double/Bool
Comparison operators§7.6==, !=, <, >, <=, >= (Int and String)
Logical operators§7.8-7.9&&, || with short-circuit evaluation
Unary operators§7.3- (negation), ! (not)
Compound assignment§7.12+=, -=, *=, /=, %=
If expression§7.4.1As statement and expression, with/without else
When expression§7.4.2With subject, without subject, comma patterns, in range, string/int matching, nested
Else-if chains§7.4.1if {} else if {} else {} (as statements)
For loop§8.2.. (inclusive), until (exclusive), downTo (descending)
While loop§8.3while (cond) { }
Do-while loop§8.3do { } while (cond)
Break and continue§7.10In for, while, and do-while loops (including nested in if)
Return§7.10Early return from functions, guard clauses (if (cond) return expr without braces)
Recursive functions§4.1Direct recursion (factorial, GCD, power), mutual recursion, multi-parameter
Function calls§7.2Direct, nested, recursive, mutual recursion, extension method syntax
printlnstdlibprintln(), println(Int), println(Double), println(String), println(Boolean), println(null)
printstdlibprint() without trailing newline — all type overloads
String templates§7.1.4"$var", "${expr}" usable anywhere (val, return, args)
Try-finally§7.4.5try { body } finally { cleanup } — finally always executes after body
String methodsstdlib.length, .uppercase(), .lowercase(), .isEmpty(), .trim(), .substring(), .contains(), .startsWith(), .endsWith(), .indexOf(), .lastIndexOf(), .replace(), .get(), .equals(), .compareTo(), .toInt(), .toDouble(), .toLong()
Type conversionsstdlibInt.toString(), Long.toString(), Double.toString(), String.toInt(), String.toLong(), String.toDouble()
Init blocks§4.5.2init { } blocks execute during construction, access constructor params
Language Server ProtocolReal-time diagnostics, semantic tokens, hover, go-to-definition, completions via skotch lsp
FeatureSpec referenceDifficultyNotes
Inheritance and override§4.5Hardopen class, override fun, super calls
Data class equals/hashCode/copy§4.5.6MediumtoString() works; equals()/hashCode()/copy()/componentN() not yet synthesized
Interfaces§4.5.3HardDeclaration, implementation, default methods
Sealed classes§4.5.5HardSealed hierarchies, exhaustive when
Generics§4.6HardType parameters, bounds, variance
Lambdas§7.2.10HardLambda literals, closures, it parameter
Safe call (?.)§3.3Donex?.length short-circuits to null if receiver is null
Non-null assert (!!)§3.3Donex!! unwraps nullable to non-null type
Type checks (is/!is)§7.6.3Doneinstanceof + smart cast narrowing in when/if with checkcast + unbox
Type casts (as/as?)§7.6.4Donecheckcast emission for safe and unsafe casts
Try/catch§7.4.5DoneFull exception tables, try-as-expression, catch variable binding (e.message), multi-statement bodies
Throw expression§7.4.6Doneathrow opcode, exception constructors (IllegalStateException, etc.), Nothing return type
CollectionsstdlibDonelistOf, map, filter, fold, 25+ HOF extensions via kotlin-stdlib.jar
kotlin.mathstdlibDoneabs, sqrt, ceil, floor, round, pow, sin, cos, tan, log, exp
readLine/readlnstdlibDonereadLine() reads stdin via Scanner(System.in).nextLine()
Varargs§4.1.2Mediumvararg parameter, spread *
Type aliases§4.7Easytypealias erased at compile time
Destructuring§8.1Mediumval (a, b) = pair via componentN()
Coroutines§7.2.11Very Hardsuspend, state machine CPS transform
Annotations§4.8MediumDeclaration, retention, reflection
Operator overloading§7.5Mediumplus, minus, compareTo, invoke
else if chains with returnMediumAll-branches-return in nested if (use when as workaround)

Top-level functions with parameters, return types, expression bodies, extension receivers, local (nested) functions, default parameter values, named arguments, and mutual recursion.

fun greet(name: String = "World"): String = "Hello, $name!"
fun configure(host: String = "localhost", port: Int = 8080) {
println("$host:$port")
}
fun Int.isEven(): Boolean = this % 2 == 0
fun main() {
println(greet()) // Hello, World!
println(greet("Kotlin")) // Hello, Kotlin!
configure() // localhost:8080
configure("example.com", 443) // example.com:443
}

Named arguments allow calling with arguments in any order:

fun rect(width: Int, height: Int): Int = width * height
rect(height = 4, width = 3) // 12, reordered to match params

Immutable val and mutable var with type inference from literals or explicit type annotations.

val message = "hello"
var counter = 0
val typed: Int = 42

Classes with primary constructors, val/var parameters, field access, instance methods, mutable field writeback, and init blocks:

class Counter {
var count: Int = 0
fun increment() { count++ }
}
fun main() {
val c = Counter()
c.increment()
c.increment()
c.increment()
println(c.count) // 3
}

You can also override toString():

class Point(val x: Int, val y: Int) {
override fun toString(): String = "($x, $y)"
}
println(Point(3, 4)) // (3, 4)

Data classes get a synthesized toString() matching Kotlin’s format:

data class Point(val x: Int, val y: Int)
data class Person(val name: String, val age: Int)
fun main() {
println(Point(1, 2)) // Point(x=1, y=2)
println(Person("Alice", 30)) // Person(name=Alice, age=30)
}
object MathUtils {
fun square(x: Int): Int = x * x
fun cube(x: Int): Int = x * x * x
}
fun main() {
println(MathUtils.square(5)) // 25
println(MathUtils.cube(3)) // 27
}
enum class Color { RED, GREEN, BLUE }
fun describe(c: Color): String = when (c) {
Color.RED -> "warm"
Color.GREEN -> "cool"
Color.BLUE -> "cool"
}
fun main() {
println(Color.RED) // RED
println(describe(Color.BLUE)) // cool
}
class Config {
companion object {
fun appName(): String = "MyApp"
fun version(): Int = 1
}
}
fun main() {
println(Config.appName()) // MyApp
println(Config.version()) // 1
}

String-to-number conversions work in expressions:

val x = "42".toInt() + 8 // 50
val y = "3.14".toDouble() // 3.14
val z = "100".toLong() * 2L // 200
val s = 42.toString() // "42"
val u = "hello".uppercase() // "HELLO"
println("hello".length + 1) // 6

Functions can accept nullable parameters (String?). The elvis operator (?:) provides a default value when the left-hand side is null:

fun greet(name: String?): String = name ?: "anonymous"
fun first(a: String?, b: String?): String = a ?: b ?: "none"
fun main() {
println(greet("Alice")) // Alice
println(greet(null)) // anonymous
println(first(null, null)) // none
}

All core control-flow constructs are implemented:

  • if/else as both statement and expression
  • else-if chains for multi-branch conditions
  • when with subject, without subject, comma patterns, in range patterns, string/int matching, else default, and nesting
  • for loops: for (i in 0..9) (inclusive), for (i in 0 until 10) (exclusive), for (i in 10 downTo 1) (descending)
  • while and do-while loops
  • break, continue, and return (including early return and guard clauses: if (n <= 1) return n)
  • try/finally — finally block always executes after try body
fun classify(n: Int): String = when {
n < 0 -> "negative"
n == 0 -> "zero"
n in 1..10 -> "small"
else -> "large"
}

Both $identifier and ${expression} interpolation forms work in regular and raw strings.

val x = 42
println("The answer is $x")
println("Double: ${x * 2}")

String instance methods are compiled to JVM invokevirtual calls:

val s = "Hello, World!"
println(s.length) // 13
println(s.uppercase()) // HELLO, WORLD!
println(s.lowercase()) // hello, world!
println(s.isEmpty()) // false
println(s.trim()) // Hello, World!
println(s.substring(7)) // World!
println(s.substring(0, 5)) // Hello
println(s.contains("World")) // true
println(s.startsWith("Hello")) // true
println(s.endsWith("!")) // true
println(s.indexOf("World")) // 7
println(s.replace("World", "Kotlin")) // Hello, Kotlin!
println(42.toString()) // 42

Compile-time constants are inlined at their call site:

const val PI = 3.14159
const val APP_NAME = "skotch"
fun main() {
println(PI) // 3.14159
println(APP_NAME) // skotch
}

println, print, maxOf, and minOf are available:

println(maxOf(10, 20)) // 20
println(minOf(10, 20)) // 10

Skotch handles procedural and object-oriented Kotlin well: console programs, algorithms, classes with constructors, mutable fields, and methods, data classes, singletons, enums, companion objects, and null-safe code using the elvis operator. Programs that use functions, control flow, basic types, Double arithmetic, and nullable parameters compile and run correctly on all five targets.

fun fibonacci(n: Int): Int {
if (n <= 1) return n
var a = 0
var b = 1
for (i in 2..n) {
val temp = a + b
a = b
b = temp
}
return b
}
fun main() {
for (i in 0..10) {
println(fibonacci(i))
}
}
enum class Priority { LOW, MEDIUM, HIGH }
data class Task(val name: String, val priority: Priority)
fun main() {
val task = Task("Deploy", Priority.HIGH)
println(task) // Task(name=Deploy, priority=HIGH)
}
fun greet(name: String?): String = name ?: "World"
fun first(a: String?, b: String?): String = a ?: b ?: "none"

Programs that depend on generics, lambdas, the Kotlin standard library collection APIs, or third-party libraries will need to wait for those features to land.