/29
Kotlin
Kotlin Compiler Construction
Nurgaliev Ildar
/29
Kotlin
Introduction● Statically typed● Compiles to JVM byte codes or JavaScript● JVM version is Java compatible both ways● Intended for industrial use
o Designed for READABILITYo Relies simple (but powerful) abstractionso Performance is a priorityo Tooling matters
● Open Source (Apache 2) o http://github.com/JetBrains/Kotlin
/29
Kotlin
Keywordsfor
while
match
case
if
then
else
do
return
fun
initial
invalvarnullcontinueStringpackageclassprivatevoidinternalprotected
/29
Kotlin
Java
type
Kotlin type
byte kotlin.Byte
short kotlin.Short
int kotlin.Int
double kotlin.Double
boolean kotlin.Boolea
n
Mapped types
Kotlin treats some Java types specially. Such types are
not loaded from Java “as is”, but are mapped to
corresponding Kotlin types. The mapping only matters
at compile time, the runtime representation remains
unchanged. Java’s primitive types are mapped to
corresponding Kotlin types (keeping platform types in
mind):
java.lang.Objec
t
kotlin.Any
!
int[] kotlin.IntArray!
/29
Kotlin
Basic syntax● You do not need ; to break statements.
● Comments are similar to Java or C#, /* This is comment */ for multi line comments and // for single
line comment.
● Unlike Java, you do not need to match your file name to your class name.
● Like JavaScript, you can create functions outside classes. So there is no need to stuff your functions as static
members of classes like what you do in C# or Java.
● Kotlin has string templates, which is awesome. e.g. "$firstName $lastName" for simple variable name or "$
{person.name} is ${1 * 2}" for any expressions. You can still do the string concatenation if you like e.g.
"hello " + "world", but that means being stupid.
● It has no tuple although Kotlin's data classes is an option to use in place of tuple.
● Use function literals to filter and map collections: basic functional programming inside
Ex: names filter {it.startsWith("A")} sortBy {it} map {it.toUpperCase()} forEach {print(it)}
/29
Kotlin
Variable semantic● There are two keywords for variable declaration, var and val.
● Use var when the variable value is to be modified and val where the variable value will not change after first
assigned.
● This val is similar to readonly keyword in C# or final keyword in Java.
● val variable must be initialized at declaration.
● Unlike Java or C#, you declare the type of a variable after the name, e.g. var firstName : String
● Number primitive types are as follows: Double, Float, Long, Int, Short, Byte. There is no automatic conversion
between types. You have to explicitly convert them.
● More primitive types: Char, String, Boolean.
● All variable declarations in Kotlin must be initialized.
● The keyword void common in Java or C# is called Unit in Kotlin.
/29
Kotlin
Identifiers4 types of identifiers supported by Scala:
● ALPHANUMERIC IDENTIFIERS
● OPERATOR IDENTIFIERS
● LITERAL IDENTIFIERS
/29
Kotlin
ALPHANUMERIC IDENTIFIERS
● An ALPHANUMERIC IDENTIFIERS starts with a letter or underscore,
which can be followed by further letters, digits, or underscores.
● '$' character is a reserved keyword
Legal alphanumeric identifiers:
age, salary, _value, __1_value
Illegal identifiers:
$salary, 123abc, -salary
/29
Kotlin
Semantic- arrays the index used in an array selection expression must be of
integer type- expressions the two operands to logical && must both be bool type, the result
is bool type- functions the type of each actual argument in a function call must be
compatible with the formal parameter- classes if specified, the parent of a class must be a properly declared class
type- interfaces all methods of the interface must be implemented if a class states
that it implements the interface
/29
Kotlin
Semantic (easy stuff)
Class names can be used before being defined• We can’t check class names– using a symbol table– or even in one pass• Solution– Pass 1: Gather all class names– Pass 2: Do the checking
/29
Kotlin
AST
– Before: Process an AST node n– Recurse: Process the children of n– After: Finish processing the AST node n
/29
Kotlin
Example AST rules● Before processing e, add definition of x to current
definitions, overriding any other definition of x● – Recurse● – After processing e, remove definition of x and restore
old definition of x
for (x : Char in “abc”){ ( x )Expressions…. ( e )}
/29
Kotlin
Symbol table operations● addSymbol(x) push x and associated info, such as
x’s type, on the stack● findSymbol(x) search stack, starting from top, for
x. Return first x found or NULL if none found● removeSymbol() pop the stack● enterScope() start a new nested scope● findSymbol(x) finds current x (or null)● addSymbol(x) add a symbol x to the table● checkScope(x) true if x defined in current scope● exitScope() exit current scope
/29
Kotlin
Recall to symbol tables for resolving
fun main(args: Array<String>) { if (args.size == 0) { println("Provide a name") return } println("Hello, ${args[0]}!")
//String Interpolation to cut down ceremony.
}
/29
Kotlin
Method declaration
● Method names have complex rules● A method need not to be defined in the class in which itis used, but in some parent class● Methods may also be redefined (overridden) (Hard point)
/29
Kotlin
Type checkingType checking computes via reasoning:If E1 and E2 have certain types, then E3 has a certain type(e1 : Int AND e2: Int) IMPLIES e1 + e2: Int
We work with hypotheses and conclusions.So we create type rules.Ex: is add minus
/29
Kotlin
Type checking
Type checking proves facts e: T– Proof is on the structure of the AST– Proof has the shape of the AST– One type rule is used for each AST node• In the type rule used for a node e:– Hypotheses are the proofs of types of e’s subexpressions– Conclusion is the type of e• Types are computed in a bottom-up pass over the AST
/29
Kotlin
Type environment before type check
The type environment gives types to the freeidentifiers in the current scope• The type environment is passed down the AST fromthe root towards the leaves• Types are computed up the AST from the leavestowards the root
/29
Kotlin
SubtypingConsider:if e0 then e1 else e2• The result can be either e1 or e2• The type is either e1’s type or of e2’s type• The best we can do is the smallest supertype larger than the type of e1 or e2
/29
Kotlin
Subtyping
lub(X,Y), the Least Upper Bound of X and Y, is Z if– X Z AND Y Z⩽ ⩽Z is an upper bound– X Z’ AND Y Z’ IMPLIES Z Z’⩽ ⩽ ⩽Z is least among upper bounds
so the least upper bound of two types is theirleast common ancestor in the inheritance tree
/29
Kotlin
Subtyping
Ex 2: The rule for case expressions takes a lub over all branches
Ex 1: if-Then-Else
/29
Kotlin
Typing methods
prelude: A method foo and an object foo can coexist in the same scope• In the type rules, this is reflected by a separate mappingM for method signaturesM(C,f) = (T1,. . .Tn,Tn+1)means in class C there is a method ff(x1:T1,. . .,xn:Tn): Tn+1 the last one is type of return st
/29
Kotlin
Polymorphism (Self type)class Count {
val i = 0;fun inc() : Count {
i++ return this }
};
inc() should return “dynamic type”
class Stock extends Count(var name:String) {};
fun main(args: Array<>String){val a = Stock(“test”)
a = (new Stock).inc (); // fail … a.name …};
/29
Kotlin
Polymorphism (Self type)– So, we must redefine inc for each of the subclasses, with a specialized return type ● Introduce the keyword SELF_TYPE to use for the return value of such
functionsSELF_TYPE is not a dynamic type– It is a static type– It helps the type checker to keep better track of types– It enables the type checker to accept more correctprograms
/29
Kotlin
SemanticElse one Hard aspect:1) Lamda expressions as new feature of the language:
val positiveNumbers = list.filter {it > 0}
Правила редукции типовых и без типовых лямбда исчислений
Область видимо-сти. Операционная семантика, виды редукций.Представление термов без использования имён (индексы де Брауна)Безопасность, продвижение, сохранение.Простое типизированное лямбда-исчисление: Типы функций.Отношение типизации. Свойства типизации. Соответствие Карри–Говарда(введения устранения, при совпадении вычисление).Стирание типов и типизируемость