Kotlin is the best(*)

home

 

Kotlin is the official language of Android devices. It compiles into Java, which was the old Android language. For extra fun, Kotlin also compiles into javascript since there weren't already enough languages doing that.

The design goals seem to be: 1) be the opposite of Java, even though it is Java, 2) add Python-like features in the most awkward way possible, and 3) add not-very-useful shortcuts to keep away outsiders.

Inferred semi-colons

Kotlin borrows the semicolon rules from Scala (another language based on Java). You can put semicolons in the usual places, but don't need them if the compiler can figure things out. It's so annoying:

var n=0  // no need for a semi-colon (but it's legal)
n+=6; n=4  // 2 on same line needs semicolon

// here it can tell the 1st line isn't done, but 2nd is:
var f : MutableList<Double> =
  mutableListOf(1.0, 2.0)

// no semi-colon needed to end the 1st print b/c of the else:
if(n>=0) print("A") else print("b")

Variables, declarations, casts

Basic Types, declare

Kotlin uses the usual script-like syntax to declare: start with "var" and write the type at the end if it's not obvious, or if you feel like it. Somewhat like Apple's Swift, constants are declared by replacing var with val:

var n:Int = 2
var n=2  // shortcut since 2 is obviously an Int

var w:String = "cow"
var w="cow"  // likewise

var num=7.0 // 7.0 is implicitly a double

// this is a constant (val, not var):
val catName : String = "Ms. Boots"

As usual, everything has a definite type. This isn't Python where any var can hold any type at any time.

For a scripting language Kotlin has a "heavy" set of types. Integers include Int but also Long, Short, and Byte. Decimal values get both Float and Double. We even have Char. Note that all types start with caps. Also, bool is written out, Boolean.

casts

Officially Kotlin doesn't have implicit casts, but it does. It's pretty inconsistant. Assignment statements won't upcast, but math expressions will:

var x:Double = 6  // error: 6 is an Int, must use 6.0
var w:String = 'x'  // error: 'x' is a char

print( 9/4.0 )  // 2.25. integer 9 WILL upcast to 9.0
var w = "cow"+8+'x' // "cow8x". Int and Char convert to String

A big suprise is that "character math" (char + int) gives a char:

var f = 'a'+5 // the character f!! 5 past 'a'
var c = 'f'-3 // the character c. 3 before f

Explicit casts are odd. You call a special member function:

3.toDouble()  // 3.0
var n=2; n.toDouble()  // 2.0
Math.sqrt(7.0).toInt() // 2

var w="56"; w.toInt() // 56
w.toIntOrNull() // also 56, but won't crash on "abc"

// Even chars and bools have them:
'1'.toInt() // 49 (ASCII value)
(4>2).toString()  // "true"

Sadly, bools won't cast to ints: true.toInt() is an error.

Lists

The array-backed list type is "MutableList" (that's going to be fun to type over and over). It has all of the basics, including grow and shrink. There's no shortcut to create one, use the function mutableListOf(i1, i2, i3):

var L : MutableList<Double> = mutableListOf(5.6, 7.1, 5.0)

L[0]  // 5.6 -- indexing is normal
L.size // really??, Not length??

L.add(50.0)  // add to end (of course)
var n=L.removeAt(0)  // remove item at [0], also returns it
L.remove(7.1)  // searches for that item

// the trick to make an empty list:
var L1 = mutableListOf<Int>()

Kotlin builds in the really cute trick where you can use a passed-in function to fill in each slot of an array (it takes the index as the input). Strangely, it's used like a constructor:

// create length 3 list with 10, 20, 30:
var L2 = List<Int>(3, {i->(i+1)*10})  // 10,20,30
// NOTE: {i->(i+1)*10 } is a simple anonymous function

You can also convert a range into a list: (1..10).toMutableList().

As you might guess, simply List represents an immutable list. I suppose Kotlin was written at the height of the immutable craze.

Tuples

You get Pair and Triple classes to make mixed-type contants:

var p1 : Pair<Int,String> = Pair(2,"bird")
var p2 = Pair(2,3)  // implicitly Pair<Int,Int>

// accessed using .first and .second:
var b1=p1.first
p2.second=5  // ERR -- immutable

Tuples cast to lists the way you'd expect: Triple(1,3,5).toList().

If you want longer mixed-type lists you can borrow from Java and use a List<Any>. You decode them with a subtype cast:

var L = mutableListOf("coat", 7, false)
// L is a MutableList<Any>

var n0 = L[0] as String
var n1 = L[1] as Int

More list Ops

There's no built-in slicing syntax. Instead L.slice(2..5) will do it (2..5 is a range). For fun, + concatentates lists and - removes all occurances of the 2nd list from the 1st:

L+listOf(5,6,9)  // adds three items (5,6,9) to end of L

L=L-listOf(2,3) // Wow! removes all 2's and 3's from L

Contains is done with the standard x in L. But "is not contained in" has a non-standard !in keyword:

if(4 in listOf(2,3,5,6)) ...
var hasCow:Boolean = "cow" in W

if(4 !in L) // if 4 is not contained in L. Ouch!

More list-likes

The primative array type is named "Array". It can't be resized and is mutable. Along with the general Array<type> we get shortcuts such as IntArray and BooleanArray:

var A : Array<Double> = arrayOf(1.4, 3.0, 9.0)
A.size  // 3, same syntax as lists
A[0]+=2.0  // arrays are mutable

// built-in shortcut for Array<Boolean>
var B : BooleanArray = booleanArrayOf(true, true, false, true)

The hash-table class uses the name "Map" from Java. As you might guess, they can be created whole using mapOf(), but the syntax for associating items looks terrible:

var M = mapOf("kitty" to "cat", "puppy" to "dog")
M["kitty"]  // cat
M["puppy"]  // dog
M["pony"] = "horse"  // at least we add items the normal way

// with the type explicit:
var NumWords : Map<Int,String> =
  mapTo(1 to "one", 2 to "two", 3 to "three")

Maps allow in and the dreadful !in. But you may not need them since looking for an illegal value returns null: M["kid"] won't crash if there's no "kid".

multi-assign

Multi-assign from a list, pair, or array is allowed, but only in a declaration:

var (a,b) = Pair(5,9)  // a=5, b=9

var(n0,n1)=L  // n0=L[0]  n1=L[1] L may be longer

There's no rule that the sizes need to match -- the input can be longer. The extra items are ignored. The actual name of this trick in Kotlin is a destructuring declaration.

if's, loops

if statements are the usual C-like syntax:

if(n1!=n2) {}  // a normal if
else {}

But they can also return a value. Kotlin doesn't have the ?: trinary operator since it already has one.

// value-returning if:
s = if(n>0) { +1 } else { -1 }

// We can have statements before the answer. Yow:
w = if(n!=8) { print("A"); "good" }
    else { "bad" }
// assigns w, might also print something

while (and do-while, bleh) loops work the normal way. Like Python, there's no regular for-loop, and a for loop is really a for-each:

// basic list loop:
for(n in L) print(n)

// optionally supply the type:
for(w:String in listOf("ant","beaver","condor")) ...

// each char in a string:
for(ch in "abcd") print(ch+",")  // a,b,c,d,

// use a range:
for(n in 1..10)
// NOTE: the ending # is IN the range

Ranges only go forwards. To go backwards you'll need to replace the two dots with downTo. Yuck:

for(x in 1..10)  // forwards
for(y in 10 downTo 1)  // yuck. backwards

for(z in 10..1) // this never runs, range is empty

Intervals other than 1 are given with the word step (something I haven't seen since Pascal, for good reason):

for(x in 10..100 step 5)  // 10 15 20 ... 100
for(y in 100 downTo 10 step 5)  // 100 95 ... 15 10

In a burst of silliness, until omits the final item (similar to Python's 1...<n):

for(n in 1 until 10)  // 1 through 9

// used as a range in a slice/substring:
var commaPos=w.indexOf(',')
// everything before index of the comma:
var beforeComma = w.slice(0 until commaPos)

Kotlin allows you to fake Python's (index,value) loops. Take your list L and use L.withIndex(). You get an iterator:

for((i,v) in L.withIndex())  // i is index, v is value

You'd think withIndex() gives you Pair tuples. But it's actually a special IndexedValue object.

Kotlin couldn't be a terrible language without a funny switch statement, renamed when. It's got various cutsie ways to select and, like if's, can be value-returning:

// a Kotlin switch statement:
var w = when(n) {
  1 -> "single"
  2,3 -> "several"
  in 4..8 -> "many"
  !in 9..1000 -> "unknown"
  else -> "defualt string"
}

functions

Functions start with the keyword fun and put the return-type at the end:

fun addOne(n:Int):Int { return n+1 }

The void type is renamed Unit. It uses the new-style trick making it a proper type with a single value (Unit), allowing you to assign from "void" functions:

// no return value:
fun printRepeating(w:String, times:Int):Unit {
  var res=""
  for(i in 1..times) res+=w;
  print(res)
}

printRepeating("goat", 4)
var n = printRepeating("arf-",3)
// n is type Unit with value Unit

Every terrible language has a special function shortcut to simply return a value. In Kotlin it's an =, omit the curly-braces, and leave out the return type if it can be guessed:

// short function syntax:
fun add1(x:Int) = x+1

// abusing value-returning if's:
fun sign(n:Double) =
  if(n==0.0) 0.0 else if(n>0.0) +1.0 else -1.0

Function pointers

Declaring a function variable isn't too bad: var f1:(Int,Int)->Int. Assigning to it or creating an anonymous function is where things get funny. The easy way to write an anonymous function is like a normal function without the name:

var f1:(Int,Int)->Int =
  // an obvious anonymous function:
  fun(a:Int, b:Int):Int { return a+b }

f1(3,7)  // 10

Of course they have a shortcut "return value only" syntax, using completely new rules:

// anonymous function using "return a value" shortcut:
f1 = { a,b -> a+b }
f1(5,6)  // 11

Those curly-braces are required. But oddly, there can't be parens around the inputs (but you need parens there when delclaring a function pointer. Arg!) Here's one more, using another value-returning if:

f1 = { a,b -> if(a>0) b+10 else b*10 }

Finally, a truely insane rule. To aim a function pointer at a regular function, you need a double-colon in front:

// a normal function for us to point at later:
fun avg(x:Int, y:Int):Int { return (x+y)/2 }

f1 = avg  // ERROR
f1 = ::avg  // good lord, this is correct

Receiving and using passed-in functions is actually fine. Here we get a function which select items from a list:

// a function taking another function as input:
fun select(L:List<Int>, isWanted:(Int)->Boolean):MutableList<Int> {
  var Res = mutableListOf<Int>()
  for(n in L)
    if(isWanted(n)) Res.add(n)
  return Res
}

For fun, let's call it with input functions in each of the 3 ways:

var L=listOf(5,6,7,8,9,10,11)

// normal anon-function systax to get multiples of 3:
select(L, fun(a:Int):Boolean { return a%3==0 })  // 6,9

// short-function syntax to select even numbers:
select(L, { n -> n%2==0 } )  // 6,8,10

// with a function-pointer:
var tenToTwenty:(Int)->Boolean = { n -> n>=10 && n<=20 } 
select(L, tenToTwenty)

// with a pre-made function to get perfect squares:
fun isSquare(n:Int):Boolean {
  for(i in 1..n/2) if(i*i==n) return true
  return false
}

select(L, ::isSquare)  // pass named function w/double-colons

Nullable types

Kotlin was part of a contest to make the weirdest rules for nullable types. It did very well. It starts off fine: ? after a type makes it nullable:

var c1:Cat = null  // error, not nullable
var c1:Cat? = null

var n:Int? = null
n=6

var n1:Int = n // error -- can't cast Int? to Int

Kotlin uses ? as the "abort without crashing on null" operator in the usual way. This will attempt to check c1.claws.length (assuming Cat and claws were declared as nullables):

if(c1?.claws?.length<5) ...

So far so good, but then it gets weird. The "unless the first thing is null" operator changes from ??, which everyone uses, to ?::

// start with a normal and a nullable String:
var w:String? = "corn"  // nullable
var w2:String = ""  // non-nullable

// w2 is w, unless w is null, in which case it's "none":
w2 = w ?: "none"

Casting a nullable into a non-nullable of the same type uses a postfix double-bang:

// w is nullable, w2 isn't:
w2 = w!!  // compiles, but throws an error if w is null

Then it goes from weird to very weird. When you sucessfully test for non-null, the type changes into the non-nullable version. For example, after the IF below, n changes from Int? to Int:

fun maybeAdd(n:Int?):Int {
  if(n==null) return 0
  // n has now changed from Int? to Int
  return n+1
}

This saves time in some cases, but hardly seems worth such a confusing special rule.

classes

Kotlin classes don't actually have member variables -- everything is a property with a hidden backing field. But they look like normal fields:

class Cat {
  var name:String=""
  var claws:Int=8
}

var c1=Cat()
c1.claws+=2; // claws is 10

You can add public, private and protected. Inun-Java-like manner, the default is public.

Constructors start with the word constructor, but are otherwise normal:

class Cat {
  var name:String=""
  var claws:Int=8

  constructor(w:String) { name=w }
}

Oddly, that style is called a secondary constructor. The "primary" constructor is this weird but cute 2-part thing. Just after the class name, list parameters in parens. That does nothing by itself. but you can use them to initialize the variables. Here we use a "primary constructor" to set the cat's name:

// "primary" constructor:
class Cat(w:String) {  // a constructor taking string w,
  var name:String = w  // using w from the constructor above

The real point of a primary constructor is an all-in-one shortcut to declare a field and give it a (primary) constructor. To do this, add var in front. This Cat has a name field:

// establishes "name" as a field
// also provides a constructor which assigns it:
class Cat(var name:String="") {
}

c1=Cat("fritz")  // c1.name is fritz
c1.name += "y"  // name is a regular field

If you have both types (I can't imagine why, but it's probably common) secondary contructors are required to call the primary one (uses the old C++-style):

class Cat(var name:String) { // primary constructor
  var claws:Int

  constructor(w:String, cl:Int) : this(w) { // <- call primary
    claws = if(c1>=0) c1 else 0  // normal constructor stuff
    if(name...) // name was set to w, we can play with it more
  }
// end of Cat

var c1=Cat("eddie")  // calling the primary constructor
var c2=Cat("beth",4) // secondary, which calls the primary
var c1=Cat()  // ERR. Currently no constructor like this

Finally, you're allowed to write start-up code in init's, which run when it's created. This uses inits to adjust the name:

class Cat(var name:String)  // all-in-1 constructor for name
  init { print("primary constructor has begun") }
  init {
    if(name=="flully") name="fluffy"
    print("we can have more than one init. Why?")
  }

  // optional secondary constructor use a # as a name:
  constructor(n:Int) : this("Number " + n.toString()) {
    print("I happen after primary constructor and all inits")
  }

As best I can tell, Kotlin prefers "primary" constructors, especially for simple data classes. Then init's are for simple corrections (i.e: can't be less than 0) which are impossible otherwise. Then real constructors are there since there are some things the "primary" constructor just can't do.

Adding properties isn't that bad. Declare what looks like a field, but write a get and set after:

class Cat {
  var kilos:Double=0.0  // <-arrg! the .0 is required

  var pounds:Double  // a pure property
    get() { return kilos*2.2 }
    set(n) { kilos=n/2.2 }

c1.kilos=10.0
c1.pounds  // 22.0, calling the get
c1.pounds=60  // calling the set. kilos is ... ummm ... 25?

Notice how there aren't curly-braces around the get/set.

As mentioned, normal fields are also properties with a hidden backing field. If you like, you can add a get/set afterwards. The keyword field refers to your own personal backing field:

// adding get/set after a "field":
class Cat {
  var name:String=""
    get() { return field }
    set(w) { if(w!="") field=w else field="no name" }

You can distinguish regular properties from variables-with-properties by whether they use field. That's also how the Kotlin compiler figures it out (also, you can't initialize properties with =).

Member functions work normally:

  // Cat member function:
  fun nameLen():Int { return name.length }

Subclassing is also mostly normal. Mostly. Kotlin classes are non-inheritable unless they start with open. Functions aren't virtual unless you mark then: another open and then override in subclasses:

open class Animal {  // this open mean inheritable
  open fun eats(n:Int):Int { ...  // this open means virtual


class Cat : Animal() {
  override fun eats(n:Int):Int {
    val baseEat=super.eats(n) // prefix "super" to use parent
    ...
  }

The mysterious extra () after the second Animal is required to call the constructor for name. Arrg.

Kotlin interfaces are the same as in Java: the keyword is interface. As usual they can't do anything (but they're allowed to write bodies for functions).

The same whacky "checking is casting" trick works with subtypes. After you test for a subclass with is, the variable changes to the subtype:

fun animalStuff(a:Animal) {
  if(a is Cat) {
    // a is now a Cat variable, this is legal:
    print(a.claws)
  }
  ...
}

You can also check for subtypes with the nullable as?:

val c1 : Cat? = a as? Cat?
if(c1!=null) print(c1.claws)

Note the second line uses another Kotlin trick: c1 starts as a nullable but turns into a non-nullable after if(c1!=null). That's why we can use c1.claws and not c1.?claws.

You can also use as, but it crashes if you're not that subtype. as? is much more useful.

Magic contexts

Kotlin provides 2 versions of aliasing variables. The keyword let is a regular alias, making it stand for your variable:

// uses "it" as an alias for catCount:
catCount.let { if(it>10 || it<1) print("out of range") }

// more useful:
L[0].size.let {  // "it" is a copy of L[0].size
  if(it==4) ...
  if(it>12) ...
}

To choose a name besides it, add that name and a ->:

L[i].let { n->  // n is alias for L[i]
  var x=n*2
  if(n<0) ...
}

The other type of alias is like a "using" with the keyword run. Mention the variable at the start and use it's fields "naked":

myWord.run {
  if(length>=2) {  // runs myWord.length
    var start = slice(1..2)  // runs myWord.slice
  ...
}

// same thing:
if(myWord.length>=2) {
  var start = myWord.slice(1..2)
...

You can use that to set fields in a clever way:

// set Cat c1's name and it's claws:
c1.run { name="alice"; claws=5 }

It gets better. Both of those are value-returning (if you want them to be). let and run return whatever the curly braces say. There are 2 variants which return the orginal variable (named also and apply).

Here's a cutesy use of also which prints a debug line as it returns n:

return n.also { print("return value is "+it) }

apply is used for chaining object mutations together. Here we add two items to L in the first apply and remove something in the second:

L.apply {
  add("cow")
  add("duck")
}.apply { // operates on L (with the previous changes):
  remove("lizard")
}

Finally, instead of myVar.run you can use with(myVar). They work the same.

File-structure and Java

Kotlin expects to see a top-level fun main(), optionally with an Array<String> parameter. Each other file is expected to have package myPackage at the top, which other files include. Ex. of a main file and a Cat class:

// === hello.kt
import Cat.Cat

fun main() : Unit {
  println("global var n="+n)

  var c1 = Cat()
  c1.age=35
  c1.nm="toby"
  println("name="+c1.nm+" age="+c1.age)
}

var n:Int=6  // globals are allowed

// === Cat.kt
package Cat

public class Cat {
  var age : Int = 0
  public var nm : String = "arf"
}

We'd compile all of this using the kotlinc command, but note it goes into a JAR file:

kotlinc hello.kt Cat.kt -include-runtime -d hello.jar

The result, hello.jar runs like the regular old Java file it is:

java -jar hello.jar

 

 

Comments. or email adminATtaxesforcatses.com