Imperative Kotlin
Imperative programming is a style of programming where an explicit set of commands are executed in order. The first major programming languages were imperative. e.g. Fortran, COBOL but were initially difficult to scale to large programs (see spaghetti code). In 1968, Edsger Dijkstra penned a famour letter to the editor, “GOTO considered harmful”, that popularized the use of control-flow. Specifically, goto statements were replaced by control flow (if/then/else) and block structures (do/while/until) and programs became much more modularized. This was a necessary step towards building larger, more complex programs.
Let’s look at a simple imperative C++ program, and see how we can convert it to a Kotlin imperative application.
Example: Calculator
#include <iostream>
#include <string>
using namespace std;
// prototypes
int add(int int1, int int2);
int subtract(int int1, int int2);
int multiply(int int1, int int2);
int divide(int int1, int int2);
// entry point
int main(int argc, const char* argv[]) {
// check for the correct number of args
if (argc != 4) {
cout << "Usage: operator int1 int2" << endl;
return 0;
}
// parse command line args
int command = std::stoi(argv[1]);
int intBuf1 = std::stoi(argv[2]);
int intBuf2 = std::stoi(argv[3]);
// process the command
switch(command) {
case 0: // add
cout << add(intBuf1, intBuf2);
break;
case 1: // subtract
cout << subtract(intBuf1, intBuf2);
break;
case 2: // multiply
cout << multiply(intBuf1, intBuf2);
break;
case 3: // divide
cout << divide(intBuf1, intBuf2);
break;
default:
cout << "Invalid Command" << endl;
}
cout << endl;
return 0;
}
// functions
int add(int int1, int int2){
return int1 + int2;
}
int subtract(int int1, int int2){
return int1 - int2;
}
int multiply(int int1, int int2){
return int1 * int2;
}
int divide(int int1, int int2){
return int1 / int2;
}
The execution path for the program looks like this:
- Check to ensure we have sufficient arguments from the command-line.
- Parse arguments (as ints).
- Switch based on the operation.
- Perform the operation and return result.
The operators (+ - * /) are described using integers, likely because of the limits of the switch statement.
Here’s the Kotlin version.
fun main() {
println(calculate(5, "*", 10))
}
fun calculate(op1: Int, operator: String, op2: Int): String {
return (
when(operator) {
"+" -> (op1 + op2).toString()
"-" -> (op1 - op2).toString()
"*" -> (op1 * op2).toString()
"/" -> (op1 / op2).toString()
else -> "Unknown operator"
}
)
}
The differences are interesting:
- The arguments are passed in as an array, but Arrays in Kotlin have properties, including size (no need for
argc
to track the length of an array). switch
in Kotlin can switch on a String (so we can use symbols for the operators).switch
is actually an expression that returns the value of the case statement.
To build and execute this program, we need to:
- Compile into a
jar
file containing our classes, and the Kotlin libraries (which are not included in the JDK). - Run using the
java
command. We can pass arguments from the command-line.
$ kotlinc calc.kt -include-runtime -d calc.jar
$ java -jar calc.jar 5.5 + 6.5
12.0
Optionally, we can create a shell script to launch our application with arguments, and provide a cleaner syntax.
$ cat calc
#!/usr/bin/env bash
java -jar calc.jar $@
$ chmod +x ./calc
$./calc 5.5 + 6.5
12.0
Andrew Bailey, David Greenhalgh & Josh Skeen. 2021. Kotlin Programming: The Big Nerd Ranch Guide. 2nd Edition. Pearson. ISBN 978-0136891055.
Bruce Eckel & Svetlana Isakova. 2020. Atomic Kotlin. Leanpub. https://leanpub.com/AtomicKotlin
JetBrains. Kotlin Basics Track (Free). https://hyperskill.org/tracks/18
JetBrains. Kotlin Documentation & Tutorials. https://kotlinlang.org
JetBrains. Kotlin Koans. https://play.kotlinlang.org/koans/overview
Dave Leeds. Dave Leeds on Kotlin. https://typealias.com
Venkat Subramaniam. 2019. Programming Kotlin. Pragmatic Bookshelf. ISBN 978-1680506358.