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
println ↗stdlibprintln(), println(Int), println(Double), println(String), println(Boolean), println(null)
print ↗stdlibprint() 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 methods ↗stdlib.length, .uppercase(), .lowercase(), .isEmpty(), .trim(), .substring(), .contains(), .startsWith(), .endsWith(), .indexOf(), .lastIndexOf(), .replace(), .get(), .equals(), .compareTo(), .toInt(), .toDouble(), .toLong()
Type conversions ↗stdlibInt.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 Protocol—Real-time diagnostics, semantic tokens, hover, go-to-definition, completions via skotch lsp
FeatureSpec referenceDifficultyNotes
Inheritance and override§4.5 ↗Hardopen class, override fun, super calls
Data class equals/hashCode/copy§4.5.6 ↗MediumtoString() works; equals()/hashCode()/copy()/componentN() not yet synthesized
Interfaces§4.5.3 ↗HardDeclaration, implementation, default methods
Sealed classes§4.5.5 ↗HardSealed hierarchies, exhaustive when
Generics§4.6 ↗HardType parameters, bounds, variance
Lambdas§7.2.10 ↗HardLambda literals, closures, it parameter
Safe call (?.)§3.3 ↗Donex?.length short-circuits to null if receiver is null
Non-null assert (!!)§3.3 ↗Donex!! unwraps nullable to non-null type
Type checks (is/!is)§7.6.3 ↗Doneinstanceof + smart cast narrowing in when/if with checkcast + unbox
Type casts (as/as?)§7.6.4 ↗Donecheckcast emission for safe and unsafe casts
Try/catch§7.4.5 ↗DoneFull exception tables, try-as-expression, catch variable binding (e.message), multi-statement bodies
Throw expression§7.4.6 ↗Doneathrow 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.2 ↗Mediumvararg parameter, spread *
Type aliases§4.7 ↗Easytypealias erased at compile time
Destructuring§8.1 ↗Mediumval (a, b) = pair via componentN()
Coroutines§7.2.11 ↗Very Hardsuspend, state machine CPS transform
Annotations§4.8 ↗MediumDeclaration, retention, reflection
Operator overloading§7.5 ↗Mediumplus, minus, compareTo, invoke
else if chains with return—MediumAll-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.