Shoulders of Giants
Languages Kotlin Learned From
Andrey Breslav, @abreslav
About me
How do you “invent” a new language?
What this talk is about
Please use my ideas!
Pragmatic Language for
Industry
Initially, a JVM language
So, what languages did Kotlin
learn from?
Let’s play
“Kotlin is just…”
while (a > b) {
if (foo.bar(baz)) {
...
} else {
...
}
}
Kotlin or Java?
while (a > b) {
if (foo.bar(baz)) {
...
} else {
...
}
}
without all the
CEREMONY
Kotlin is just
class C(p: Int, val pp: I1) {
var foo: Int = p
object Singleton {
...
}
}
Kotlin or Scala?
class C(p: Int, val pp: I1) {
var foo: Int = p
object Singleton {
...
}
}
without all the
NICE THINGS
Kotlin is just
without all the
implicits, higher kinds,
pattern matching, view bounds,
monads…
Kotlin is just
without all the
STUFF
Kotlin is just
mylist
.filter { it > 0 }
.map { it + 1 }
html {
head { ... }
body { ... }
}
Kotlin or Groovy?
mylist
.filter { it > 0 }
.map { it + 1 }
html {
head { ... }
body { ... }
}
class C : BaseClass, I1, I2 {
class Nested { ... }
}
Kotlin or C#?
class C : BaseClass, I1, I2 {
class Nested { ... }
}
How do you make all these ideas work together?
M1 M2
M
3
M
4
M5 M6 M9
M
10
M
11
M12 Beta 1.0
2010
Q2 Q3 Q4
2011
Q1 Q2 Q3 Q4
2012
Q1 Q2 Q3 Q4
2013
Q1 Q2 Q3 Q4
2014
Q1 Q2 Q3 Q4
2015
Q1 Q2 Q3 Q4
2016
Q1
M
14
M
13
M8
M7
Open
Source
Public
“Project Kotlin”
Whiteboard
“Jet Language”
Initial
Ideas
Java 6 Java 7 Java 8
2.8 2.9 2.10 2.11
4.0 5.0 6.0
3.1 3.3 3.4
1.8 2.0
1.7 2.1 2.2 2.3 2.4
1.0 2.0
Secret
3.2 3.5
C#
Key ideas
Their origins
Fitting them together
+ Kotlin’s unique features
Scratching the surface
Syntactic choices
Leaving things out (Java)
• No C-style for loops
for (int i = 1; i < 10; i++)
• No ternary conditional operator
a > b ? c : d
• No assignment expression
if (a = b) ...
• No bit shift operators
a >> b
• No bitwise and/or/xor operators
a | b & c ^ d
Optional semicolons (Scala)
a = a + b
a =
a + b
a = a +
b
a = a
+ b
a = (a
+ b)
self-assignment
Types on the right, val/var/fun (Scala, …)
val typed: Int = 1
var inferred = 2
fun function(p: Int) = 1 + p
Everything is an expression (Scala)
val foo = if (a > b) c else d // sorry, no a > b ? c : d
val foo = when (a) {
1, 2, 3 -> bar()
4 -> baz()
}
val foo = try { bar() } catch (e: Exception) { baz() }
val foo = bar?.baz() ?: return
Note: for, while and do ... while are not expressions
Leaving things out (Scala)
• Invisible lambdas
list.map(_.foo)
• ASCII art
10 |+| Monoid[Int].zero
def name_=(n: String) { … }
• Arrays look like functions
arr(0)
Making ideas work together
val person = Person("Jane", "Doe")
val anonymous = object : Base() {
override fun foo() { ... }
}
Making ideas work together (Python)
val person = Person("Jane", "Doe")
val anonymous = object : Base() {
override fun foo() { ... }
}
Functions and Properties
Break out of the OOP cage
Top-level (free) functions and properties
val p = 1
fun main() { … }
Top-level (free) functions and properties
@file:JvmName(“Main”)
val p = 1
fun main() { … }
Unit, Void and Nothing
Java/C#/C++/C:
void main() { return; }
Kotlin:
fun main(): Unit { return }
fun TODO(): Nothing = throw Exception()
Extensions in C# and Scala
implicit class RichPerson(p: Person) {
def fullName = s"${p.firstName} ${p.lastName}"
}
class PersonUtil {
public static string FullName(this Person p) {
return $"{p.firstName} {p.lastName}"
}
}
Why extensions?
• Monkey-patch libraries?
• Break encapsulation?
• Get rid of *Util classes?
• Nicer code completion?
Why extensions?
• Monkey-patch libraries?
• Break encapsulation?
• Get rid of *Util classes?
• Nicer code completion?
Keep APIs Minimal
Extensions in Kotlin
class Person(
val firstName: String,
val lastName: String
)
fun Person.fullName() {
return "$firstName $lastName"
}
Higher-Order Functions
fun List<A>.map<A, B>(f: fun (A): B): List<B>
• Where is A declared?
• Did I close all the parentheses?
Higher-Order Functions
fun List<A>.map<A, B>(f: fun (A): B): List<B>
fun <A, B> List<A>.map(f: (A) -> B): List<B>
Dangling lambdas (Groovy)
list.map({e -> e.foo}).filter({e -> e.bar})
list.map { it.foo }.filter { it.bar }
lock (foo) {
…
}
DSLs: Type-Safe Builders
html {
body {
a(href = "http://my.com") {
img(src = "http://my.com/icon.png")
}
}
}
With function
val foo = new Foo()
foo.bar()
foo.baz()
foo.qux() with(foo) {
bar()
baz()
qux()
}
Extension function types
fun <T> with(subj: T, body: T.() -> Unit)
with(foo) {
this.bar()
this.baz()
this.qux()
}
with(foo) {
bar()
baz()
qux()
}
Not Kotlin J
10 PRINT "Welcome to Baysick Lunar Lander v0.9"
20 LET ('dist := 100)
30 LET ('v := 1)
40 LET ('fuel := 1000)
50 LET ('mass := 1000)
60 PRINT "You are drifting towards the moon."
Not Kotlin J
object Lunar extends Baysick {
def main(args:Array[String]) = {
10 PRINT "Welcome to Baysick Lunar Lander v0.9"
20 LET ('dist := 100)
30 LET ('v := 1)
40 LET ('fuel := 1000)
50 LET ('mass := 1000)
60 PRINT "You are drifting towards the moon."
}
}
https://www.scala-lang.org/old/node/1403
Operator overloading + Conventions (C#)
operator fun A.plus(B): C
operator fun Foos.iterator(): MyIt
operator fun MyIt.next(): Foo
operator fun MyIt.hasNext(): Boolean
for (foo in foos) {
…
}
Generics
Parametric polymorphism in OO-languages
Angle Brackets
Java
new ArrayList<Foo>()
Collections.<Foo>emptyList ()
Scala
new List[Foo]()
emptyList[Foo]()
arr(0)
foo(bar<A, B>(x + 1))
How many (named) functions?
foo(bar<A, B>(x + 1))
foo(bar < a, b > (x + 1))
Angle brackets? There’s no such thing!
C#, Kotlin
ArrayList<Foo>()
emptyList<Foo>()
arr[0]
* foo((bar < A), B > (x+1))
Java
new ArrayList<Foo>()
Collections.<Foo>emptyList ()
Scala
new List[Foo]()
emptyList[Foo]()
arr(0)
A bit of history
• Functional languages: no classes, just generic functions
• C++: templates 😱
• Java 1.0, C# 1.0: cast away
• Person p = (Person) personList.get(0)
• Pizza, Scala: Erased generics
• C# 2.0: Reified generics
• Java 5: Erased generics + Raw types
• Java 7: List<Foo> foos = new ArrayList<>()
Use-site Variance (Java)
void copyTo(Bag<? super Animal> dest) {
...
}
fun copyTo(Bag<in Animal> dest) {
...
}
Declaration-site Variance (Scala)
interface Read<out T> {
fun read(): T
}
interface Write<in T> {
fun write(t: T)
}
trait Read[+T] {
def read(): T
}
trait Write[-T] {
def write(t: T)
}
Mixed-site Variance
<T> void copy(
List<? extends T> from,
List<? super T> to
) { … }
fun <T> copy(
from: List<T>, to: MutableList<in T>
) { … }
Type-safety and friends
Nullables, Smart Casts, Platform Types
From Nice or Gosu: Smart casts
val foo = getNullable()
foo.bar() // forbidden!
if (foo != null) foo.bar()
if (foo == null) return
foo.bar()
Groovy and C# dealt with nulls already
val middle = p.middleName ?: "N/A"
persons.firstOrNull()?.firstName ?: "N/A"
nullable!!.foo
(foo as Person).bar
(foo as? Person)?.bar
Scala’s pattern matching
val greeting = x match {
case Person(f, l) => s"Hi, $f $l!"
case Pet(n) => s"Hi, $n!"
_ => "Not so greetable O_o"
}
Kotlin’s “pattern matching”
val greeting = when (x) {
is Person -> "Hi, ${x.first} ${x.last}!"
is Pet -> "Hi, ${x.name}!"
else -> "Not so greetable o_O"
}
val (a, b) = pair
operator fun component1() = first
operator fun component2() = second
Multiple inheritance
Initialization is more complex than one might think
Java 7’s take on inheritance
One superclass
• Static fields and methods
• Even static overrides!
• Instance fields and methods
• public
• protected
• package-private
Many superinterfaces
• Order doesn’t matter
• Static fields and methods
• Only public
• Instance methods
• Only public
• Only abstract 😪
Scala’s traits & C++ classes
• Many super-traits
• Instance methods with bodies
• Instance fields with initializers
• No constructor parameters
• Supertype order matters 😱
• Many superclasses
• Instance methods with bodies
• Instance fields
• Constructors with parameters
• Virtual base classes 😱
• Private/Protected base classes
“Jet Language” and Multiple inheritance
• Many superclasses
• Always generate class + interface
• Emulate inheritance through delegation
• Instance methods with bodies
• Instance fields with initializers
• Constructors with parameters… 😱
Big surprise: Delegation can’t cut it
Early Kotlin got traits instead
Like Scala traits, but no state
• Instance methods with bodies
• Compiler tricks in the subclasses
• Binary compatibility wrt separate compilation
• Instance properties
• No backing fields
• Getters and setters are as good as methods
• Supertype order doesn’t matter 🎉
And… Java 8 enters the scene
No point in calling them “traits” any more
• Rename to ”interfaces”
• Keep the pre-Java 8 tricks for Android
• Some tricky corner cases, but most users never noticed 😉
Companion objects
Trying to get rid of statics
From Scala…
object Singleton : Base(foo) {
val bar = ...
fun baz(): String { ... }
}
Singleton.baz()
Companion objects (Scala)
class Data {
...
}
object Data {
private var count = 0
def create() = ...
}
val data = Data.create()
Companion objects
class Data private constructor(val x: Int) {
companion object {
fun create(x) = Data(x)
}
}
val data = Data.create(10)
Companion objects
class Data private constructor(val x: Int) {
companion object {
@JvmStatic
fun create(x) = Data(x)
}
}
val data = Data.create(10)
BTW: Not so great ideas
• Companion objects L
• Inheritance by delegation L
• No ternary if L
What else I would love to tell you about
(another time)
• Platform types
• Nullables + Generics + Mutability + Raw/Array/Dynamic
• Coroutines
• A lot of power, mostly as a library
• Delegated properties
• And other conventions
• Inline functions
• Very safe macros
• …
What we talked about
• Languages Kotlin learned from
• Making ideas from different languages work together
• Fixing common pitfalls
Takeaways
Please ask me
questions!
P.S. My Personal Workaround
In practice, languages are often
selected by passion, not reason.
Much as I’d like it to be the other way around,
I see no way of changing that.
So, I’m trying to make Kotlin a
language that is loved for a reason J
How people argue about languages
• Language A is awesome!
• It's taken all of its awesomeness from language B
• What's the meaning
• Are B and A equal?
• No to me, I already know and love B, so I think I don't need A
• But you can use A as much as you like

2022 May - Shoulders of Giants - Amsterdam - Kotlin Dev Day.pdf

  • 1.
    Shoulders of Giants LanguagesKotlin Learned From Andrey Breslav, @abreslav
  • 2.
  • 3.
    How do you“invent” a new language?
  • 5.
    What this talkis about Please use my ideas!
  • 6.
  • 7.
    So, what languagesdid Kotlin learn from?
  • 8.
  • 9.
    while (a >b) { if (foo.bar(baz)) { ... } else { ... } }
  • 10.
    Kotlin or Java? while(a > b) { if (foo.bar(baz)) { ... } else { ... } }
  • 11.
  • 12.
    class C(p: Int,val pp: I1) { var foo: Int = p object Singleton { ... } }
  • 13.
    Kotlin or Scala? classC(p: Int, val pp: I1) { var foo: Int = p object Singleton { ... } }
  • 14.
    without all the NICETHINGS Kotlin is just
  • 15.
    without all the implicits,higher kinds, pattern matching, view bounds, monads… Kotlin is just
  • 16.
  • 17.
    mylist .filter { it> 0 } .map { it + 1 } html { head { ... } body { ... } }
  • 18.
    Kotlin or Groovy? mylist .filter{ it > 0 } .map { it + 1 } html { head { ... } body { ... } }
  • 19.
    class C :BaseClass, I1, I2 { class Nested { ... } }
  • 20.
    Kotlin or C#? classC : BaseClass, I1, I2 { class Nested { ... } }
  • 21.
    How do youmake all these ideas work together?
  • 22.
    M1 M2 M 3 M 4 M5 M6M9 M 10 M 11 M12 Beta 1.0 2010 Q2 Q3 Q4 2011 Q1 Q2 Q3 Q4 2012 Q1 Q2 Q3 Q4 2013 Q1 Q2 Q3 Q4 2014 Q1 Q2 Q3 Q4 2015 Q1 Q2 Q3 Q4 2016 Q1 M 14 M 13 M8 M7 Open Source Public “Project Kotlin” Whiteboard “Jet Language” Initial Ideas Java 6 Java 7 Java 8 2.8 2.9 2.10 2.11 4.0 5.0 6.0 3.1 3.3 3.4 1.8 2.0 1.7 2.1 2.2 2.3 2.4 1.0 2.0 Secret 3.2 3.5 C#
  • 23.
    Key ideas Their origins Fittingthem together + Kotlin’s unique features
  • 24.
  • 25.
    Leaving things out(Java) • No C-style for loops for (int i = 1; i < 10; i++) • No ternary conditional operator a > b ? c : d • No assignment expression if (a = b) ... • No bit shift operators a >> b • No bitwise and/or/xor operators a | b & c ^ d
  • 26.
    Optional semicolons (Scala) a= a + b a = a + b a = a + b a = a + b a = (a + b) self-assignment
  • 27.
    Types on theright, val/var/fun (Scala, …) val typed: Int = 1 var inferred = 2 fun function(p: Int) = 1 + p
  • 28.
    Everything is anexpression (Scala) val foo = if (a > b) c else d // sorry, no a > b ? c : d val foo = when (a) { 1, 2, 3 -> bar() 4 -> baz() } val foo = try { bar() } catch (e: Exception) { baz() } val foo = bar?.baz() ?: return Note: for, while and do ... while are not expressions
  • 29.
    Leaving things out(Scala) • Invisible lambdas list.map(_.foo) • ASCII art 10 |+| Monoid[Int].zero def name_=(n: String) { … } • Arrays look like functions arr(0)
  • 30.
    Making ideas worktogether val person = Person("Jane", "Doe") val anonymous = object : Base() { override fun foo() { ... } }
  • 31.
    Making ideas worktogether (Python) val person = Person("Jane", "Doe") val anonymous = object : Base() { override fun foo() { ... } }
  • 32.
    Functions and Properties Breakout of the OOP cage
  • 33.
    Top-level (free) functionsand properties val p = 1 fun main() { … }
  • 34.
    Top-level (free) functionsand properties @file:JvmName(“Main”) val p = 1 fun main() { … }
  • 35.
    Unit, Void andNothing Java/C#/C++/C: void main() { return; } Kotlin: fun main(): Unit { return } fun TODO(): Nothing = throw Exception()
  • 36.
    Extensions in C#and Scala implicit class RichPerson(p: Person) { def fullName = s"${p.firstName} ${p.lastName}" } class PersonUtil { public static string FullName(this Person p) { return $"{p.firstName} {p.lastName}" } }
  • 37.
    Why extensions? • Monkey-patchlibraries? • Break encapsulation? • Get rid of *Util classes? • Nicer code completion?
  • 38.
    Why extensions? • Monkey-patchlibraries? • Break encapsulation? • Get rid of *Util classes? • Nicer code completion? Keep APIs Minimal
  • 39.
    Extensions in Kotlin classPerson( val firstName: String, val lastName: String ) fun Person.fullName() { return "$firstName $lastName" }
  • 40.
    Higher-Order Functions fun List<A>.map<A,B>(f: fun (A): B): List<B> • Where is A declared? • Did I close all the parentheses?
  • 41.
    Higher-Order Functions fun List<A>.map<A,B>(f: fun (A): B): List<B> fun <A, B> List<A>.map(f: (A) -> B): List<B>
  • 42.
    Dangling lambdas (Groovy) list.map({e-> e.foo}).filter({e -> e.bar}) list.map { it.foo }.filter { it.bar } lock (foo) { … }
  • 43.
    DSLs: Type-Safe Builders html{ body { a(href = "http://my.com") { img(src = "http://my.com/icon.png") } } }
  • 44.
    With function val foo= new Foo() foo.bar() foo.baz() foo.qux() with(foo) { bar() baz() qux() }
  • 45.
    Extension function types fun<T> with(subj: T, body: T.() -> Unit) with(foo) { this.bar() this.baz() this.qux() } with(foo) { bar() baz() qux() }
  • 46.
    Not Kotlin J 10PRINT "Welcome to Baysick Lunar Lander v0.9" 20 LET ('dist := 100) 30 LET ('v := 1) 40 LET ('fuel := 1000) 50 LET ('mass := 1000) 60 PRINT "You are drifting towards the moon."
  • 47.
    Not Kotlin J objectLunar extends Baysick { def main(args:Array[String]) = { 10 PRINT "Welcome to Baysick Lunar Lander v0.9" 20 LET ('dist := 100) 30 LET ('v := 1) 40 LET ('fuel := 1000) 50 LET ('mass := 1000) 60 PRINT "You are drifting towards the moon." } } https://www.scala-lang.org/old/node/1403
  • 48.
    Operator overloading +Conventions (C#) operator fun A.plus(B): C operator fun Foos.iterator(): MyIt operator fun MyIt.next(): Foo operator fun MyIt.hasNext(): Boolean for (foo in foos) { … }
  • 49.
  • 50.
    Angle Brackets Java new ArrayList<Foo>() Collections.<Foo>emptyList() Scala new List[Foo]() emptyList[Foo]() arr(0)
  • 51.
    foo(bar<A, B>(x +1)) How many (named) functions?
  • 52.
    foo(bar<A, B>(x +1)) foo(bar < a, b > (x + 1))
  • 53.
    Angle brackets? There’sno such thing! C#, Kotlin ArrayList<Foo>() emptyList<Foo>() arr[0] * foo((bar < A), B > (x+1)) Java new ArrayList<Foo>() Collections.<Foo>emptyList () Scala new List[Foo]() emptyList[Foo]() arr(0)
  • 54.
    A bit ofhistory • Functional languages: no classes, just generic functions • C++: templates 😱 • Java 1.0, C# 1.0: cast away • Person p = (Person) personList.get(0) • Pizza, Scala: Erased generics • C# 2.0: Reified generics • Java 5: Erased generics + Raw types • Java 7: List<Foo> foos = new ArrayList<>()
  • 55.
    Use-site Variance (Java) voidcopyTo(Bag<? super Animal> dest) { ... } fun copyTo(Bag<in Animal> dest) { ... }
  • 56.
    Declaration-site Variance (Scala) interfaceRead<out T> { fun read(): T } interface Write<in T> { fun write(t: T) } trait Read[+T] { def read(): T } trait Write[-T] { def write(t: T) }
  • 57.
    Mixed-site Variance <T> voidcopy( List<? extends T> from, List<? super T> to ) { … } fun <T> copy( from: List<T>, to: MutableList<in T> ) { … }
  • 58.
    Type-safety and friends Nullables,Smart Casts, Platform Types
  • 59.
    From Nice orGosu: Smart casts val foo = getNullable() foo.bar() // forbidden! if (foo != null) foo.bar() if (foo == null) return foo.bar()
  • 60.
    Groovy and C#dealt with nulls already val middle = p.middleName ?: "N/A" persons.firstOrNull()?.firstName ?: "N/A" nullable!!.foo (foo as Person).bar (foo as? Person)?.bar
  • 61.
    Scala’s pattern matching valgreeting = x match { case Person(f, l) => s"Hi, $f $l!" case Pet(n) => s"Hi, $n!" _ => "Not so greetable O_o" }
  • 62.
    Kotlin’s “pattern matching” valgreeting = when (x) { is Person -> "Hi, ${x.first} ${x.last}!" is Pet -> "Hi, ${x.name}!" else -> "Not so greetable o_O" } val (a, b) = pair operator fun component1() = first operator fun component2() = second
  • 63.
    Multiple inheritance Initialization ismore complex than one might think
  • 64.
    Java 7’s takeon inheritance One superclass • Static fields and methods • Even static overrides! • Instance fields and methods • public • protected • package-private Many superinterfaces • Order doesn’t matter • Static fields and methods • Only public • Instance methods • Only public • Only abstract 😪
  • 65.
    Scala’s traits &C++ classes • Many super-traits • Instance methods with bodies • Instance fields with initializers • No constructor parameters • Supertype order matters 😱 • Many superclasses • Instance methods with bodies • Instance fields • Constructors with parameters • Virtual base classes 😱 • Private/Protected base classes
  • 66.
    “Jet Language” andMultiple inheritance • Many superclasses • Always generate class + interface • Emulate inheritance through delegation • Instance methods with bodies • Instance fields with initializers • Constructors with parameters… 😱 Big surprise: Delegation can’t cut it
  • 67.
    Early Kotlin gottraits instead Like Scala traits, but no state • Instance methods with bodies • Compiler tricks in the subclasses • Binary compatibility wrt separate compilation • Instance properties • No backing fields • Getters and setters are as good as methods • Supertype order doesn’t matter 🎉
  • 68.
    And… Java 8enters the scene No point in calling them “traits” any more • Rename to ”interfaces” • Keep the pre-Java 8 tricks for Android • Some tricky corner cases, but most users never noticed 😉
  • 69.
    Companion objects Trying toget rid of statics
  • 70.
    From Scala… object Singleton: Base(foo) { val bar = ... fun baz(): String { ... } } Singleton.baz()
  • 71.
    Companion objects (Scala) classData { ... } object Data { private var count = 0 def create() = ... } val data = Data.create()
  • 72.
    Companion objects class Dataprivate constructor(val x: Int) { companion object { fun create(x) = Data(x) } } val data = Data.create(10)
  • 73.
    Companion objects class Dataprivate constructor(val x: Int) { companion object { @JvmStatic fun create(x) = Data(x) } } val data = Data.create(10)
  • 74.
    BTW: Not sogreat ideas • Companion objects L • Inheritance by delegation L • No ternary if L
  • 75.
    What else Iwould love to tell you about (another time) • Platform types • Nullables + Generics + Mutability + Raw/Array/Dynamic • Coroutines • A lot of power, mostly as a library • Delegated properties • And other conventions • Inline functions • Very safe macros • …
  • 76.
    What we talkedabout • Languages Kotlin learned from • Making ideas from different languages work together • Fixing common pitfalls
  • 77.
  • 78.
    P.S. My PersonalWorkaround In practice, languages are often selected by passion, not reason. Much as I’d like it to be the other way around, I see no way of changing that. So, I’m trying to make Kotlin a language that is loved for a reason J
  • 79.
    How people argueabout languages • Language A is awesome! • It's taken all of its awesomeness from language B • What's the meaning • Are B and A equal? • No to me, I already know and love B, so I think I don't need A • But you can use A as much as you like