Tip:
Highlight text to annotate it
X
ANDREY BRESLAV: Good afternoon everybody,
thank you for coming.
This is a Kotlin session, and my name is Andrey Breslav.
I'll be very happy to tell you about my project.
So this is a modern programming language for JVM,
as it was originally designed.
Now, it's not only for JVM, but for JavaScript as well.
So we have two platforms that we compile to.
And this is like an advanced introductory talk, meaning
that I will adjust the material
according to your interest.
So if you ask no questions, it will be boring.
I'll just give you very silly examples.
So go ahead and ask questions whenever you like.
I'll start a very short motivational part, and we'll
quickly proceed to code examples and all
the relevant stuff.
So the greatest question for any programming language
designer would be, why are you doing this crazy thing?
Nobody ever designs programming languages from
scratch, right?
So our context is JVM, and if you think about the Java
platform as a whole, it was started in 1995, and since
then it has gained lots of popularity, primarily as a
platform, as an ecosystem of many libraries, many
frameworks, great virtual machines, and
so on and so forth.
And all that progressed pretty good over time.
So now we have a really up to date platform, but the
language didn't progress that well over time.
So if you remember, the 1990s, my monitor looked
something like this.
This may be not a 1995 design, but a 1998 design, and this is
what I would call Java, which is-- a great
language of its time.
So if you consider state of the arts, it is a little bit
ahead of all what we have on the Java platform, even of
what we will have in a year from now with the great Java 8
coming and rescuing the [? world. ?]
So there is a lot to be fixed or reconsidered, and we're
trying to do that.
So we're aiming at something I like to formulate as a modern
language for industry, meaning that we want to be up to date
in the sense that we want to be smart enough to handle your
code properly.
And I think the main challenge there is to have in your code
enough information for the human being to understand what
he's doing, and not much more.
So if your compiler is much more stupid than
you are, it's bad.
And we're trying to fix that problem.
The biggest thing there is to have the right abstractions
provided by your language so that you don't have to repeat
yourself over and over again, going over the same coding
pattern, or the same idiom because you just can't
abstract it over and put into the library.
So the point there is to be able to attract such things,
put them into the libraries, and just call them instead of
recoding them by hand over and over again.
And industry is our real life setting and our my main
constraint because you can fantasize about so many
interesting things-- that you can automate this, and fully
analyze this, and have completely safe code, and
everything.
But as soon as you consider like 30 people working on the
project of millions lines of code for 10 years, it's all a
little different.
Because we get to read much more code than we write, and
it has to be maintainable by [INAUDIBLE] not only by the
discipline of the coders.
Because it's long history and people
may vary their attitudes.
So this is our set constraints.
So if you take JetBrains as an interested party here, we have
our code base, 10 years old, written in Java, having tens
of thousands of classes there.
And we would really like to have a language to take this
the whole code base, without rewriting it, just adding new
things in a much nicer way.
Does that makes sense?
Any questions?
Objections?
All right.
So there are definitely is a question or a set of
questions--
why not this or that language?
I have some names called out here, but you may be
interested in some particular one.
So asking why not about whatever you like.
AUDIENCE: Why not ML?
ANDREY BRESLAV: Why not ML?
Very easy--
not Java compatible.
Why not Scala?
So we tried Scala.
We actually tried a few languages before
we started our own.
Our the two biggest problems with Scala is, one,
philosophical, relying on implicits so much that you
don't see so many things in the code that are actually
there and working for you, and this has lots of the location
of different sorts, but I think the most important is
this comprehensibility implication, where you have
lots of things expressed in a very powerful way, but in such
a way that you don't see them.
So for a large project it can be a real disaster.
Also this has implications on performance of the compiler,
performance of the IDE, and so on and so forth.
But these are like technical details, and the biggest issue
there would be just the reliability of the code.
And then another thing is like more technical, but still
pretty important--
there is no Scala IDE today, and it's been a long time
since people started doing those IDEs.
Scala people are doing their IDE for eight
years by now, I think.
And we are doing ours for, like, over for four years
already, and nobody got anywhere.
And this is a very alarming thing.
I mean we may be very lazy and not doing the Scala plug-in,
but Scala people are doing their IDE pretty well, and it
seems to be an inherently complex problem to develop an
IDE, which suggests that, to comprehend the language is a
very difficult task, even for a computer.
So this is, in short, our problem with Scala for our
setting, having a huge code base, many developers and so
on and so forth.
More questions like this.
AUDIENCE: What about Groovy?
ANDREY BRESLAV: What about Groovy?
So Groovy is actually a whole different story because it's
mainly a dynamic language.
So Groovy 2.0 has this nice feature of static compilation,
which, to me, mostly turns it into a Java with a little bit
of syntax additions.
So Groovy is ideologically a dynamic language.
So if you want to use it in a Groovy way, it will be
dynamic, thus slow.
So you can't actually run a huge code base, like--
you can't run anything like an IDE written in Groovy, just
because, by nature, [? it can't ?] be fast.
And also, with dynamic languages,
there is another problem.
If you have a huge project, and you want to refactor it,
you're just done there.
I mean, you can't do it by hand, of course.
But this is the only way you can do it.
And this is strongly against our culture to refactor
anything by hand.
So Groovy's not our way.
Yeah, so Kotlin is primarily a static language, so we are
planning to maybe, at some point, add dynamic types to
handle some interesting situations.
But this is not, like, the main focus of the language.
The main focus is statically typed, type safe language.
More questions?
All right.
Then just a short explanation of where we are now.
Kotlin is not yet released.
We have open sourced the project in
February of this year.
And currently, we're going through a couple of milestones
before we get to the beta stage.
The current phase is M3.
We have most of the features prototyped and working.
There are many bugs to fix, and there are many performance
problems to fix, but we're approaching this beta phase
where it's going to be a stable compiler usable for
production, and we're going to use it [? a J brains ?]
at that point.
But we're not going to commit to this design as, like,
something we're going to maintain backwards
compatibility for.
So we're first rolling it out a JetBrains for internal
valuation, of course, it's open source, and
everybody can use it.
But the point is to evaluate it in an industrial setting
and then specify anything and release.
Because before you actually try the design, you can't be
really sure that it's good for your setting.
Questions here?
All right.
Go ahead.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: What's the relation
between Kotlin and MPS.
MPS, Meta Programming System, is more of a research project
by JetBrains.
It's a language workbench where you can create your
languages, which are not actually textual, but look
like textual, and you can extend them in a
nice way and easily.
There is no connection between these two projects.
We learn from each other more in the requirements part, not
the implementation approaches.
So these are two parallel projects.
MPS is more like a research thing, although there are
projects in production that are written in MPS.
And there are none in Kotlin yet.
So yeah, these are two parallel things.
More questions?
All right, then I proceed to the code.
Yeah, so I'm using my intelligay with the Kotlin
plug-in here, and here's the first example.
Any questions?
No questions, OK.
So here's one thing I'd like to point
out about this program.
The code is organizing the packages.
And we have a function which sits right under the package.
So a function in Kotlin doesn't have to be inside a
class, it may be just a free function like it is here.
Now I'll just quickly demo basics syntax, and we'll
proceed to some interesting examples.
Now I'm creating a class here, a class caled Greeter with a
function greet and saying hello.
Google, this is what a class looks like, pretty
straightforward.
And here I create an instance of this class
and call this function.
And we have code navigation, and all the basic
IDE stuff is ready.
Another thing I would like to do is to show how
variables look like.
So this is a local, immutable variable.
If you know Scala, it looks familiar.
And many other things look familiar if you know Scala,
Groovy, C#, Java, or many other languages.
So we learn from our colleagues
in different groups.
So here's an immutable local, and this is now
an immutable member.
And one more thing I made, is a constructor parameter.
So here is my primary constructor, a constructor
declared right in the header of the class.
So this variable here can be used inside the initialization
code, like, I can put it here, and it will be inside the
value of this s variable.
I'm renaming s to something more reasonable.
Does that make sense?
Any questions?
Go ahead.
The question is, can I call this from Java?
Yes I do.
Yes I can.
So if I just create a Java class here, just created the
Java class here, so here's my Java class,
and I say new Greeter.
Here it is even in the completion.
And I can say greet, and I have to pass something here.
It will say Hello Java to me, hopefully.
I have an error.
OK, I have to put something here too.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Is there a default visibility to what?
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Oh, OK.
All right, yeah, so let me explain this.
First of all, a visibility of classes and everything else.
By default, it's internal.
Internal means it's private to your module.
A module is this entity here.
So Kotlin examples here is my module.
So it's wider than a package.
About return types, so this field here, it's actually a
property, not a field, actually has a
type, but it's inferred.
So you can say string explicitly, or just omit the
type because it's inferred from the right hand side.
For a function, if you don't declare anything here, you
just have unit by default, which is returning nothing
interesting.
It's like void, but a little better than void.
It's a proper type.
So by default, you just unit.
If you want something else, you specify the type.
Go ahead.
AUDIENCE: [INAUDIBLE]
The question is, how do [? cold ?]
Kotlin's package level functions from Java.
Let's have a function here.
Here's a function foo.
And from Java, so this package, actually, is compiled
down to a class with static members.
So if we have a look at the byte code, this is what it
looks like.
Let me just put it here.
And here's my foo.
It's just a public static function in my package class.
And the class has name hello.namespace anyway.
So if I say here
hello.namespace.foo, there it goes.
More questions?
AUDIENCE: [INAUDIBLE]?
ANDREY BRESLAV: If you name a class namespace, you just
don't parse because its security.
Or do you parse?
Oh, that's a shame.
All right, yeah, yes, you do--
sorry.
We used to call packages namespaces and at that time,
it didn't parse.
Now it should be an error, but it's not there yet.
I'm having my times mixed up, sorry.
More questions?
Print line is a function in our library package which is
imported by default.
So here it's just implemented like this, and the package is
Kotlin I/O, which is just by [? default ?] there.
More questions?
The question is, are functions first class objects that you
can pass around?
So yes and no.
So currently, we don't have a syntax to all you just refer
to function name as a value.
It will be there, but it's not there yet.
And you can't pass a function around if you wrap it into an
anonymous function and pass it.
That is a value.
There's any way to pass things like functions around on the
JVM, you have to create an object.
What the function types look like?
OK, I'll get there in some minute.
I'll be demoing that.
Yes, there was an inline keyword, which I'm not sure is
right on that function.
But it's not a keyword.
It's an annotation--
another not yet implemented feature, which will probably
be in Kotlin like one point something.
The point here is that we want to support inlining as a
compiler capability to eliminate
closure allocation, sorry.
In many situations that would be very beneficial--
when you inline the whole function
together with its arguments.
And the [? Jit ?] can't do that.
So we want to do that ourselves.
Jit can't inline the code together with its argument.
All right, so shall I proceed to the next thing?
So that was the boring thing.
Another boring thing comes here.
So this is a class, which is analogous to
something like this.
So this is a Java class whose particular property is that it
does nothing.
Even doesn't fit on the screen.
And this is unfair, because you have to repeat yourself
all over again, like first name, first name, first name,
first name, first name, and first name here inside
parentheses.
Kind of bad--
you've really convinced the compiler that you want that
first name.
So we're trying to work around this problem somehow.
And this is a very bad piece of Kotlin, where you say first
name, first name, first name three times.
You don't want to do that.
You simply declare your three constructor parameters here,
and then you have three vals in the class, and you just
initialize them this way.
So you can get the same result by putting vals right here.
This way you just get a data class, basically, something
that holds your data and does nothing else.
And it doesn't say anything else, which is probably good.
Questions?
The default?
Why wouldn't we make having a val here a default?
Well, I don't think that an average class only holds data.
So there is an option of saying, whatever you pass to
the constructor is stored there, but it would be making
implicit the fact of allocating a new field in your
class, which is a little bit against our beliefs.
We're trying to make all the relevant things explicit.
If you want some data to be stored in an object, you have
to say that it's stored there.
So this val tells you that it is stored, not just used for
initalization.
Yes.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: OK, so currently, for this exact
class, [INAUDIBLE] and hashcode are just inherited
from the base class.
But if you say that this is a data class, data is just an
annotation that tells the compiler to generate a few
convenience methods.
It automatically emits the correct equals, hashcode, and
[? two string ?].
AUDIENCE: [INAUDIBLE].
ANDREY BRESLAV: Doesn't matter.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: OK, the question is, how do I specify
whether this first name has a setter or getter or whatever.
So if you say there it means that you have a field which
nobody sees.
It's a private field.
You can't access it.
And you have a getter.
So if you say val, you have a getter.
If you say var, or a mutable thing, you have a
getter and a setter.
You can even customize those, but in the body of the class.
Yeah, so If you want to change types of things and store
referenced to mutable objects internally and expose them as
immutable things, it would be necessary to have two names
declared for that.
So a private property holding a mutable version, and a
public thing for a [? read-only ?] version.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yes, you can.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yeah, but one thing there is that is that
the getter has to have the same type as the property.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Ah, you mean, OK, yeah, it was a
misunderstanding.
So for me, a read only thing has a read only type, like a
read-only interface.
And you want only to decorate the return value.
Yeah, yeah, sure you could.
Yeah, OK.
This you can do.
Go ahead.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: You can provide your own annotation.
So we don't have a fixed API yet to program a compiler
plug-in that handles the annotation.
But the overall architecture is there.
So you can declare your own annotations and put them here.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: It's just a class name.
So I think I can even navigate to it.
So here is the data annotation.
It's a class [? somewhere. ?]
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yeah, any annotation that's applicable
to this kind of declaration.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Well, to me, it looks better this way.
And we are actually going to make public also an annotation
just to get rid of the stupid keywords.
So it looks right.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yeah, they can have constructor parameters,
and you can just put them there.
OK, so I'll demo some syntax here.
Actually not syntax, but use cases for
different kinds of functions.
So this is how you create a list, like a list literal.
In Kotlin, this array list is just a
function with a var parameter.
And this is how you iterate through the list.
It should be pretty straightforward.
OK, now let's do the same thing with the map.
It's a little bit more fancy syntax.
So we have a function hash map that takes, actually, it just
takes a sequence of pairs.
And this two function takes the left thing and the right
thing, and makes a pair of them, and passes
that to the hash map.
So you can construct a map just by passing a bunch of
pairs there.
Then a map can be accessed with square brackets, which
are actually just calls to get and set methods.
So these are just convention calls.
Most operators in Kotlin are treated by convention.
So if you say minus, it's actually a method called minus
declared somewhere.
And same for square brackets.
Square brackets for reading is get, and for writing is set.
So here it's called a set method on a map.
And this is how you iterate over a map.
You can declare a pair key and value and say, for those pairs
in your map, you want to process every pair and handle
it somehow.
The question is if we have overloading--
unfortunately, yes.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: If you have multiple getters, they would
have either different types of parameters or different
numbers of parameters.
All right, so in your square brackets, you
can put many arguments.
So it will just overload.
It will perform the overload resolution on this
call, and that's it.
Are the generic types erased?
Unfortunately, yes.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: The question is, can you confuse a map for
an array because it has numbers as keys.
Yes you can, and I don't think it's bad.
[LAUGH]
Yeah, so an array access looks exactly like a map access or
whatever access.
Like a list access would look the same way because you have
a get method--
on array, on lists, on map, on whatever.
And a string axis will look exactly the same.
You have a string, and you say string square bracket I, and
it will be a character with this number.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Good point.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yes, yes, currently, any class that
declares a method called get can be
accessed with square brackets.
We are going to restrict this a little bit with the
demanding an annotation on the get that says, OK, this is an
operator, not just a function, to manage this somehow.
But generally the idea is that you can make any class into
supporting this get convention.
Two is a function that looks like this.
I'm not explaining this a right now, it
will be in a few seconds.
But basically, it's a function that takes a and b
and returns a pair.
OK.
AUDIENCE: [INAUDIBLE]
operator, you have a generally [INAUDIBLE].
ANDREY BRESLAV: All right.
The question is if we have primitive types--
yes and no.
So for the language itself, it doesn't know about any
primitive types.
It thinks everything's classes.
But when we generate Java code, we actually have
primitive integers there, and primitive doubles,
and stuff like that.
So it's purely an
implementation detail over JVM.
The question is, could you use a
primitive as a type argument.
Yes, you could.
And again, on the JVM, it would be integer for int and
character for [? char, ?] and so on.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Exactly like Java.
Yeah, with aux, unfortunately.
We can avoid a little bit of boxing sometimes,
but not much of that.
Now this is a platform specific thing.
We can't do much about it.
OK, a short explanation about arrays.
So arrays are quite an unfortunate story on JVM.
So we have nine array types in the language.
One is array of t, which is an array of whatever object, and
the other eight are int array, char array,
whatever other array.
So if you want an array of ints, you say int array
instead of array of int.
If you say array of int, it will be array of integer.
So this is not very nicely looking, but I think arrays
are, actually, low level implementation,
optimization tools.
So you don't really want them in your APIs anyway.
So if you want something nice and shiny, it better use
collections anyway.
So this is not a huge loss, although I
would like them uniform.
But we can do that.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Great question.
Thank you very much.
So the question is, is this a tuple?
I'm going to answer it in a second.
So here is my next example.
So that was not actually a tuple.
We have dropped tuples like a few weeks ago because they are
not needed.
So this was something else.
We'll call it a multi declaration, where you can
assign something to a number of variables.
So how it works is, by the way, note that this is Java
AWT point, which is just an existing class,
not a Kotlin class.
So the thing is that, if you say x and y gets point of 1
and 2, it gets compiled to x gets point of 1 of and 2 dot
component 1, and a y gets point component 2.
And component 1 and component 2 should be some functions
available on the point class.
The problem is that point class doesn't have those
functions declared in its class file, but we have
something called extension functions.
Here is the illustration of what they look like, which
lets you extend your classes with static methods like here
to make those methods available after you're done.
So for this point object, I can say point of 1 and 2, dot
component 1.
And this is all we need to be able to say x, y
gets point 1 and 2.
This component 1 is declared here.
It's a special kind of definition syntax.
So you have a funky word, then you have the
receiver type point.
And then, after a comma, the function name.
This means that this is an extension function that takes
its first argument on the left of the comments, purely a
syntactical convention, not anything, like, very fancy.
It's basically the same utility function you would put
in your point util class, and it's
implemented as a static function.
There is no, like, altering the class at runtime or no
dynamic dispatch, or anything.
But still, it makes this kind of syntactic convention
possible, where you say, whatever has and component 1
after a dot will be able to use this
multi-declaration syntax.
And the same holds for many other language conventions,
like fours and stuff.
I'll show that in the second.
Questions here?
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: OK, so here we are declaring two
variables, x and y.
OK, yeah, thank you.
That's a great question.
The question is, where does this x come from?
It's actually this dot x, and this is, as usual, the object
you're called--
right, so this is just the receiver of your function.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: All right, yeah.
So we're getting into a little bit of confusion.
So this is implemented as a static function.
It actually belongs to this package.
It's not in the class.
But it's declared to have this point receiver type.
And this makes the compiler create a variable called this
inside this function of this type.
And that's it.
So it's not a member of the class, but it
has the same syntax.
And you can pretend you are in the class, although you, of
course, don't have any access to private members, for
example, or anything like that.
But the syntax is the same.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yes, if you have more component functions,
it doesn't invalidate this two declaration syntax.
You are able now to say x, y, and z gets point of whatever.
But you're not prohibited to say x and y.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Currently, we do not allow
you to skip a component.
This wouldn't compile.
But we will do that.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yes, unfortunately yes, it's not
only public, it's mutable.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yeah, you could of course say this dot
get x or whatever.
So this is what AWT looks like.
They have public, mutable fields.
Can't help it.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: OK, another concept gets on stage.
The question is, is this like aspect oriented programming?
Well, yes and no.
Aspect j had these intertype decorations, where you could
say, OK, this method now belongs to that class.
But the difference here is that, in aspect j, that method
actually got to that class.
So you would alter the bytecode of that class at
runtime, or even statically, and actually
push the method inside.
We don't do that.
We only have the syntax that reminds you of that one.
But it's not exactly the same thing.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yeah, good question.
Why dropping tuples, and why not using general pattern
matching instead of this silly convention?
I'll answer it in a second.
Let's go over it, maybe, easier questions.
Yeah.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: OK.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: I wouldn't say it's compromising
the language design.
So it turns out, while it may sound a little bit
hacky, but it's not.
It turns out that we want to extend existing APIs by our
own functions because we tend to operate a lot on existing
data types, for example.
And the natural way of saying, I want to call this on that
object, is to say object dot this function.
It has a bunch of advantages.
One of which is, you can say this object dot and get the
whole API in your completion list, unless you're using dim
or something.
But some people use IDEs, actually.
So this is one thing.
Another thing, I have a demo for that.
I was planning to show it later, but you are
predating my demos.
So here is a piece of Guava, just to show the difference
between the two approaches.
So Java's collections don't have proper, at least today,
don't have proper methods for transforming and filtering.
So the only thing we can do is to create this collections two
a class, and have transform and filter there.
And so this is how you transform and then filter a
collection.
This is the best we can do without
this extension mechanism.
But with an extension mechanism, we
get something else.
We can say list.filter.map, or transform, or
whatever you call that.
And the huge difference between this and that is that,
here, what you say is transform a filter of a list.
And the subject list is lost somewhere in the middle.
And your operations start getting backwards.
As opposed to here, where you say list, transform it this
way and then that way.
So it gets you the right understanding of how you
actually protest the data.
And I think it's important.
Yeah.
Did I answer the question?
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: All right.
Thank you.
Now we're getting back to--
Oh, OK.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: It is a static function that takes a point as
an argument.
So it's a component of point.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Almost, yeah.
Yeah, so it's basically like a function of the class.
There are two differences.
First of all, you can import it or choose
not too import it.
And then you don't have it.
And second, you can't override it because it is static.
OK.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: It's all static.
So if you have component one declared on object, it has an
implementation that will be run.
Whatever it does, I don't know.
What's the component one of the object.
But anyway, it's all static.
All right, so now back to the tuples question.
Let's have a look.
So there are two huge usages for tuples.
One is returning multiple things from a function.
Another is pattern matching.
First things first.
So here is an example of returning two
things from a function.
So I want to parse a file name and a line
number from a string.
Here it is, and I just assign it to a pair of things here.
So this is what my function looks like.
I say, I return a location, and then I do some regular
expressions and say location of whatever--
I return a location.
So what would be the difference
with returning a tuple.
I could say, by the way, a pair of, whatever,
string and int here.
So what's the difference between returning my location
and a pair?
There is a huge difference.
A pair is some object with two properties
there, first and second.
And you have no idea what is first and what it second.
So when I call this, I have to navigate back to the
declaration of this function and see what is first and what
is second to figure out what to do with them.
And my location has proper names for the component so
that I never get lost in those orderings and stuff like that.
So it's documented by default, by definition.
So I think this is a huge advantage of the named things
over tuples.
We thought about having a [? decent ?] tuples with
labels, which are mostly like records, already, that
actually store the naming
information in the type itself.
But those are so much like classes that it's funny to
have this concept in the language alongside classes.
AUDIENCE: [INAUDIBLE]
and the value and error codes really have nothing to do.
And you're [INAUDIBLE].
ANDREY BRESLAV: Yeah I agree.
So if we could actually return multiple things from a
function, physically, that would be great.
And we would probably struggle for something else.
But underneath we anyway have those classes.
But the point is different.
Yeah, so the point is that, if you have two unrelated things,
like your actual return and an error code, how do relate one
with a decent name?
Yeah, there is a problem there, I agree.
But this is the only problem.
And I think having this problem is better than having
an extra concept in the language which is totally
separated from everything else and duplicates much of the
functionality anyway.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: All right.
OK, yeah.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yes, so there is an interesting argument.
AUDIENCE: [INAUDIBLE]
OK?
ANDREY BRESLAV: OK.
All right, so the point is, what if I really need a tuple?
Like I want to put my values into map like a key maps, not
to one value, but to two values.
For that, you have a pair class in the library and a
triple class in the library.
And you don't have a quadruple class because if you have
quadruples in the code, you can't read that code.
Yeah, so the performance argument you're making, I
don't quite understand because to put anything into a map,
you have to either alter the map implementation or to have
an object there.
I don't know any other way.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: If you specialize, yeah.
If you specialize.
If you tell me how to specialize properly, I will be
very grateful.
But let's take that offline.
OK, all right, so yeah.
And the same trickery with multiple decorations is used
for, say, index iteration over a string, for example.
We can say that a string [? that ?] indexed gives us an
iterator that returns those pairs of things.
And here is a usage of a pair.
It's a proper class, not too harmful if you're not using it
as a standalone object.
This should be pretty straightforward.
I won't give any details about this.
Any more conceptual questions or objections?
I'm getting lots of objections here.
This is great.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: The question is, if you're going to have
Kotlin-specific reflection that would be properly reflect
our new concepts.
Yes, at some point we will.
So currently, we only put annotation on those functions
in the Java sense.
So that you can actually recover that information, but
we don't have a library that would do that nicely.
But the plan is to implement that, yeah.
There was another question.
Yeah, OK, yeah.
All right.
What comes next?
I showed you the collections already.
Yeah, so a little bit more on the collection story.
So there are many different approaches to collections.
And it's customary for different languages to say,
whatever platform we run on, collections are so important
that we redo the collections and have our own, incompatible
with the Java collections because those are bad and--
mutable, and stuff like that.
So we're trying to stick to Java collections because
transforming your data all the time is costly and
inconvenient.
So we have those vanilla Java collections.
We provide extension functions to equip them with all the
needed functionality.
And we do something else on top of that.
Something else looks like this.
So here is a piece of funny code.
Please tell me what's wrong with it.
Actually, it's nothing wrong.
But you should suspect that it's wrong.
I have a print function that takes a list of any-- any is
an object, like the top class.
And then I call it, here, on the list of ints.
Is it bad?
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Yeah, so Java wouldn't let me do that
because as I could say list.add of string here.
The trick is that my add does not resolve.
There's no add.
The reason is that I'm not running on a Java's list per
se, although, at run time it's just a Java's list, but on
something else.
So Kotlin's collections look like this.
Can you see this?
OK, so we have split the collection interfaces into
read only and mutable versions.
These are all erased, so in the bytecode, it's actually
Java.util.list.
But for the Kotlin compiler, it's a read-only iterable,
read-only collection, read-only set, read-only list.
And then we have mutable versions derived from those.
So that in the Kotlin type system, you can't actually
distinguish between a read-only interface and a
fully fledged mutable interface.
And Java classes are seen as extending
those mutable versions.
And in Kotlin, you can't derive your own classes from
immutable versions.
Does that make sense?
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: These are not the same things.
I meant read only.
So these interfaces do not guarantee you any
immutability.
They just don't have mutation methods.
So these are read-only interfaces.
These are mutable interfaces, and you can derive your own
immutable collections from the read-only interface.
And we are planning to provide you some easy way to reuse the
existing immutable code from Java to declare that it's
actually extending the read-only iterable, rather
than a mutable iterable.
Questions?
OK, so this is pure compiler fiction.
Nothing is actually happening at run time, but you can have
this properly covariates list and the dream comes true.
Finally, the user stops wondering why can't he pass a
list of ints where a list of any is expected.
Isn't it a list of any, after all.
Yes it is, if it's not mutable.
So that's OK.
Good, that was easy.
Now let's turn to something rather different.
So here is a piece of Java code.
This is Java, not Kotlin.
We don't say that public, static, whatever.
And this code is problematic because there is a risk of
runtime error.
These file.list files would throw and null pointer
exception if the file does not exist, or is not a directory.
And this is why lots of enterprise code gets covered
with those null checks like all over the place.
We have check that for null, and that for null,
and maybe that also.
And it may be pointless, maybe not.
But it's definitely a very, ver, very funny thing to guard
because you don't know what happens to guard against
everything.
So we're trying to manage this somehow by making the compiler
actually watch your nulls and telling you when you have to
do anything about it.
So it's the same Java I/O file here.
And we call list files, and the compiler
knows that it's nullable.
So if I say files that size, it will warn me.
Actually, it won't let me compile anything.
It will just tell me that I have to do something about
that null before I dereference the file's variable.
So actually, this file's variable has this type with a
question mark at the end, which is a
nullable array type.
And if you have a nullable type, it means that you either
have an array or null.
And if it can be both, you can just dereference.
So what you have to do is, either check, say, if files is
not null, then you can.
Or you can do many other things, actually, you could
say your files equals null, then
return, or so on so forth.
Please ask questions here, go ahead.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: We're explicitly doing analysis to
try to figure out the nulls.
So this is actually a very straightforward data flow
analysis that is built into the type system, basically.
Yeah, and we're supported for nullable types to know where
we can dereference variables and where it's risky.
AUDIENCE: [INAUDIBLE]
function.
ANDREY BRESLAV: All right, let me show you a
little bit of it.
So if you want to say, if is null of x, then do something,
this won't work unless you inline that function.
So if you inline it, we can't actually run the
analysis on this body.
Otherwise it's not safe to say it's actually
checking for non-null.
One other thing is that instead of saying return here,
you could say, say, fail, which is like j unit's fail.
And it still will work, but for a different reason.
Fail says just throw an illegal argument exception,
and it actually has a type.
And I hope my IDE can throw it out.
And the type is nothing.
So for this dairy, only for this type, we know that it's a
special type.
It's an empty type.
So if you manage to compute a value of that type, you're
probably not in the control flow anymore.
So it's equivalent to explicitly throwing an
exception or returning from your scope.
So you're guaranteed not to have a null pointer
[INAUDIBLE] reference here.
Makes sense?
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Let me repeat this.
So currently we're not inlining anything, so we're
not looking at byte codes anywhere.
For field, we're looking at its type signature.
Because it returns this special type, we know
that it will exit.
When we do inlinig, we will look at the implementation,
whatever it is, byte code, or just the code inside a
function, and figure out the data flow there.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Oh, oh right.
So this is a different story.
By default, everything from Java returns null, but there
is a catch here because this is not just a default.
By default, that would be a nullable
array of nullable file.
So this function is currently manually annotated.
So we have those annotations for JDK
and many other libraries.
And you can add annotations yourself, but we're currently
working on a tool that infers them automatically.
So there, we are looking at the bytecode of the JDK.
And--
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: We are thinking about flipping that
default after learning how to infer proper annotations, but
currently, yes.
The default is that everything is nullable unless you
annotate it explicitly.
So say, at JetBrains, in Java we're using annotations,
nullable and not null, all over the place.
So it will be easy for us.
And I had seen lots of nullable and not
null in Guava at least.
So maybe Google uses that as well.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: OK, more questions.
Oh, go ahead.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: The question is if we can use a nullable or
non-null annotations for compiler optimizations.
Unfortunately, I don't know about many ways of using them
at all because again, it's on JDM.
You just have reference so they're always nullable by
nature on the virtual machine level.
So we can't have optimized much there.
OK.
Another bit on this same static analysis we're making,
yeah, so this is a school example of [INAUDIBLE]
interpreter of expressions.
We have an expression class here with two cases of a
number constant and the sum of two expressions.
And I want an interpreter.
So this is a very bad Kotlin code.
Again, we have an eval that takes an expression and
returns an int.
And I check if, e is a num so e is just an of check, if e is
a number, then we can create a new variable.
We cast, and we call what we want on that variable.
Oh, this is really annoying.
So I'm not doing this.
In Kotlin what I do is just this.
The compiler is smart enough to figure out that we have
checked for this num here, and we can now use the variable as
if it was of that type.
Makes sense?
Good.
No questions here.
That's great.
And the same, of course, can be done for sum as well.
And if you think about it, it's very similar to pattern
matching in a away.
So if we do that in a difference syntax for a switch
statement, which we call win, here's another version of the
same function.
So what I'm saying here is, when e is a number, use it as
a number, if it is a sum, use it as a sum, otherwise, throw
an exception.
So this is our poor man's pattern matching.
We don't do fully fledged pattern matching because we're
not convinced it's really necessary.
Probably 80% to 90% of the cases are covered by this
because if you look at pattern matching in function languages
that have this feature, you encounters so many patterns
like num of v and then use v, which is absolutely equivalent
to this, where you just don't have to capture the name
because you have the name here.
So we are trying to get away without doing pattern matching
because it's a huge complex feature and it would be nice
not to have it in the language.
And if it works, we'll be just fine like this.
Type refinement, not really.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: We propagate through the control flow if it
doesn't get too crazy.
So we actually propagate less than we could, am I right?
Um, sorry, no, I take that back.
We used to propagate less than we could because we used to
declare variables this way, like somewhere
in the control flow.
But this is not there anymore.
So now we propagate as much as we can.
So it's inside a single unit of data flow, like, inside a
function or inside a constructor, things like that.
And there, Yeah, I would just take the whole control flow
graph and propagate the data over it.
I wouldn't say it interacts in any way because function
overloading happens on declaration level, and this
happens on expression level.
These are pretty disconnected, but I see what you mean.
So if you have an ambiguity because we have a function
that is overloaded, like you may be of a and of b, and
you're a [INAUDIBLE] of a, and you're checking for b, and
then just saying f of x, what would that be? f
of a or f of b?
So since this is confusing, we'll just
say ambiguity there.
We just don't do anything and let you choose because
otherwise, you'll be confused either way.
Yeah, so we could take whatever default, but the user
would be confused.
Yeah.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: It's an explicit return because here
is an equal sign.
And this is an expression that returns that evaluates to
whatever is on the right hand side of the error.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: It's can be any expression.
AUDIENCE: OK, [INAUDIBLE]
if we have, like, pair.first [INAUDIBLE].
ANDREY BRESLAV: In case of pair.first, it will because we
know that it cannot be mutated in any way.
But if it's, say, a mutable property, it wouldn't be a
learned for a new type because it may change.
Yeah, so the customer practice here is that, if you want your
information to be stored there, even for, say, a
function return value or a mutable property return, you
just assign it to a local variable and
then check the variable.
AUDIENCE: [INAUDIBLE]
ANDREY BRESLAV: Oh, by as foo, you mean like SQL.
Like, declare a variable of this name.
No, we are thinking about finally getting a let
expression into the language.
But that's hard.
Yeah, I'm a little bit out of time already, so I'm wrapping
the official talk, and we'll have Q&A. So the bottom line
here is that we have a powerful language with, I
think, very good set of abstractions that are
facilitating our job
interoperability in a nice way.
So if you're interested in anything about Kotlin, you
just go for our home page, which is kotlin.jetbrains.org.
And there you have all the documentation, and online
demos, things like that.
So please come and give us your feedback, maybe
contribute if you like.
We'll be just happy about that.
Thank you very much for your attention.
[APPLAUSE]