How do you traverse a map?

It’s been a while since I blogged last time on Kotlin M2. Now, the hot summer has passed, and M3 will be out very soon. In this post I describe two small features added in M3 that make our lives easier and will lead to simplification of the language.

How do you traverse a map?

Assume you have a map like this:

What do you do to traverse it and handle each entry? One might think of something along these lines:

This code is obviously bad, because you don’t remember which one is which: is entry.getKey() a user and entry.getValue() — a number, or vice versa (did you even notice that the two are mixed up in the message?). A better version would be to assign them to variables upfront:

This is better, but still too wordy. How about this:

Looks like what we want, but how can we do it?

Pair objects

To iterate through something, we have to supply an iterator() function. As Map doesn’t have one, it will be an extension function. What should it return? One option would be an iterator over pairs (e.g. objects of Tuple2). The obvious downside is that would need to create a new object for every entry in the map. Can we do better?

Multi-declarations in Kotlin

Kotlin M3 introduces a new concept that we call multi-declarations. You can write things like this:

A multi-declaration creates multiple variable at once. It is compiled down to the following code:

The component1() and component2() functions are another example of the principle of conventions widely used in Kotlin (e.g. operators like + and *, for-loops etc.). Anything can be on the right-hand side of a multi-assignment, as long as the required number of component functions can be called on it. And, of course, there can be component3() and component4() and so on.

The same thing works in a for-loop: when you say

Variables a and b get the values returned by component1() and component2() called on element of the collection. Goes without saying, these functions can be extensions.

Note: Multi-declarations will be available in the upcoming Kotlin M3, currently you can try them out using our nightly builds.

Back to maps

Now, let’s come back to the map example. It’s easy now: we can simple provide component functions for Map.Entry, and the example above will work.

Data classes

We frequently create classes that do nothing but hold data. As you know, Kotlin makes them as short as possible by providing primary constructors that can declare properties:

But often times we need a little more: an obvious equals()/hashCode() pair, a toString(), and now — component functions. We had quite a few requests for supporting this, and now there is data annotation supported by Kotlin compiler:

This class gets component functions, one for each property declared in the primary constructor, generated automatically (this is already implemented), same for all the other goodies common for data: toString(), equals() and hashCode() (will be there soon).

Multiple return values

How to return multiple things from a function? Some think that tuples provide a good way of doing this, but the problem there is the same as with Map.Entry: tuple components have no names, so the call site is cluttered with blank “_1″ and “_2″ or something very similar. So it seems much nicer to handle multiple returns with data classes:

No matter whether we decide to use a multi-declaration or not, the names are not lost: they are declared in the data class.

Some people complain about the need to come up with the name for the returned entity, but in many cases it is good to realize what this thing is

Conclusion

We discussed new features introduced in Kotlin M3. Multi-declarations allow to bind several names shortly, and, as an example, traverse map entries in a nice way without creating unneeded objects. Data classes enable one-line definitions of classes that simply handle data and yet come with all the necessary functions attached.

Your feedback is very welcome, as usual. For more on the new things to appear in the upcoming M3 and our future plans — stay tuned until next week.

About Andrey Breslav

Andrey is the lead language designer working on Project Kotlin at JetBrains. He also works on making the Java language better, serving as a Java Community Process expert in a group for JSR-335 ("Project Lambda"), and occasionally speak at software conferences such as Devoxx or JavaOne.
This entry was posted in General, Language design and tagged . Bookmark the permalink.

42 Responses to How do you traverse a map?

  1. Zalim says:

    Great work!
    I think return type of returnTwoThings should be TwoThings.

  2. Graham Nash says:

    Data classes and destructuring both look amazing. Data classes are a much needed addition that are just as simple as they should be. Are the data classes immutable? Destructuring a la Python looks nice as well. Is there a typo in the last code example? It looks like you are returning a Pair instead of a TwoThings? Or does Pair cast to TwoThings?

    • > Are the data classes immutable?
      Yes, if you declare all properties to be val. If you want you can make some properties mutable (they won’t contribute to hashCode()).

      > Is there a typo in the last code example?
      Fixed already

  3. MuppetGate says:

    I don’t want to start a fight, but I think this is much better than using loads of Tuple classes.

    Any chance of generating a copy function as part of the data class? Rarely used but it might be handy.

  4. beginner says:

    Looks great!!!
    But I think the numbering is incorrect. (component1 , component2 , …)
    I think it should be (component0 , component1 , … ) .

  5. Eugen says:

    I, too, find this approach to mutli-declarations/multi-return-values and to data classes great.

    Along the same line as the parent’s question, I wonder if you thought about creating some kind of ‘read’-function for every data class, that receives a (properly or not properly formatted) string representation of an appropriate data object and creates the respective new data object (or null). This would be in some ways analogous to Haskell’s ‘read’ function. Frankly, I don’t know if there will be a lot of use cases in Kotlin code for such a feature or whether an external library would be better suited for such a task.

    Also, will the copy methods (if they are going to be implemented) behave like you described here: http://confluence.jetbrains.net/display/Kotlin/Properties+And+Fields?focusedCommentId=41484439#comment-41484439

  6. wfee says:

    I would vote for copy function as well. It is very useful on Scala Case classes.

  7. zim says:

    What’s the roadmap? How many milestones are planned until 1.0, also what’s the current estimate for 1.0? 2013 1st half/2nd half, 2014?

    Kotlin looks very promising. Long ago I’ve shunned Java and favored C#. For going truly cross-platform Kotlin and fixing lots of the irritating stuff from Java, it looks very interesting. Tooling is essential, so I wouldn’t use a (complex) language without an IDE. Also, the compilation possibility to Javascript is nice, since I find JS repulsive.

    Keep it up. Cheers!

  8. zim says:

    It looks like eclipse extend and kotlin share a couple of features. What do you think are the most important features found in Kotlin but not in extend? What are the reasons one should choose Kotlin over extend? Or rather, when do you want to use xtend and when kotlin?

  9. Alex Besogonov says:

    Some way to create a data class inline would be great. Something like:

    fun returnTwoThings(): (val name : String, val age : Int) {
    // Complex computation...
    return (strId, number)
    }

    That would require automatic destructuring and restructuring.

    In the end, it’s really named tuples that you want.

    • Nobody woul be able to refer to this class…

      • Alex Besogonov says:

        That’s fine. In most cases you can just use type inference. And if you want, you can re-structure it into a named class. Or just use parametrized ‘TupleN’ classes.

        I want to be able to do something like this:

        val data = sess.from(MyTable.table).select(MyTable.table.columnA, MyTable.table.columnB);
        //Where columnA is Column and columnB is Column

        //Type of data should be List<Tuple>
        for (val f in data)
        {
        print f.columnA, f.columnB;
        }

    • Richiban says:

      I second this… It would be a more meaningful return type than “Tuple2″, because the return names and types would appear in the method signature.

      Since what you would end up with when you call this method would be an anonymous class (much like C#) you would then have to either:
      * Use type inference
      * Decollate the result (I may have just made up ‘Decollate’. I hope it’s a word)

      This would solve the problem of having to constantly write single-use throwaway classes just so that you can return multiple values from a function (although I do appreciate that you’ve tried to alleviate this with single-line record type definitions).

    • knic27 says:

      I realize this comment is very late, but I agree this would be great addition to the language. “Reply” type throw away classes suck and just clutter up the namespace. In terms of naming, you could just, say, append “.out” to the function name.

      Even better (though probably controversial) is if the data class could be inferred. One of the things I’ve *really* enjoyed in programming with clojure is storing things in maps, to which I can add key/value on the fly. This leads to much faster programming and the feeling that you don’t have to think about everything up front. E.g.

      (Let’s say * indicates that the return type is an inferred data class)

      fun makePerson(name : String, age: Int) : * {
      ….
      return (name = name, age = age)
      }

      var person = makePerson(“Bob”, 21);
      println(person.name);

      So, in the above ( name : String, age : Int ) is inferred as the return type.

      This class could also be referred to as makePerson.out.

      This reduces a lot of unnecessary boilerplate.

      Now as I code and I want to add “gender” to person, I only need to add it to one place: return (name = name, age = age, gender = gender).

      • If map suites your needs so well, why not just use a map? No classes needed at all.

        • knic27 says:

          Because a map’s types have to be all the same (or I can use an untyped map and lose type safety.) Also, the maps keys would presumably be strings, which the compiler can’t ensure that I’ve gotten correct either. With a struct/data class I just get so much better type checking than with a map. If I were content with not having the compiler check these things for me, I’d be content with clojure :) It’s specifically because I want type checking that I’m looking into Kotlin.

          • Ok, I see. Having classes “inferred” like you suggest would mean that these classes are somewhat reduced to only local use: if they have no names, there’s no way they can be referenced as types, hence the use of this feature would be rather limited. Note: deriving a name from the function’s name does not work, because of overloading and nameless functions — lambdas.

  10. Emilio Lopez Gabeiras says:

    It would be nice to allow partial extraction:

    val (a, _, c) = aAndBAndC

  11. B7W says:

    I love it. But stupid question. Why val (a, b) = aAndB, not val a, b = aAndB?

  12. Ilya says:

    Wow, you just copied some more funcitonality from Scala! :)

  13. Throwable says:

    1. This is rather dangerous to use multi-declarations as they define a strics order of fields in a class. Thus a construction like:
    val (name, pass) = getUser();
    will not fail if someone adds a second field “email” in the structure, but will not work correctly. I prefer to define field mapping explicitly. In Habr there was an idea to use something like this: val (name, pass) = getUser().(nickname, password).

    2. Data types are good. They are like structures in C. It could be useful to add by default a Serializable and Cloneable interfaces to them to denote their value-like nature.

    3. As for deprecation of tuples… Ideally I prefer to write something like this:

    fun myFunction( a : String ) : ( x : Int, y : Int ) {

    return ( 10, 20 )
    }

    I really have no reason to declare a new class that serves only for returning result and used only once. Named tuples solved this problem perfectly. Moreover a type: (String) -> (Int, Int) corresponds to all functions that return two parameters of type Int and not only my return-class. I think a good solution may be is to maintain tuples and create correspondent return-classes internally that extend them. Thus the code above may be translated to this:

    data class myFunction$result( val x : Int, val y : Int ) : Tuple2( x, y );
    fun myFunction( a : String ) : myFunction$result {

    return myFunction$result( 10, 20 )
    }

    With the same way labeled tuples may be implemented.

    • 1. It won’t break only in the rather pathological case of encoding everything with the same type
      3. The notion that you can simply use “anonymous classes” instead of tuple is a little naive. Can I override such a method? What if I say (10, 20) somewhere outside the “return” expression? Are “(10, 20)” here and “(10, 20)” in another function equal? Etc…

      • Simon says:

        1.
        I also find it a bit scary that if the return type changes from
        data class User(val firstName:String, val lastName:String)
        to
        data class User(val firstName:String,
        val middleName:String, val lastName:String)
        then code like
        val (first, last) = getUser()
        will continue to compile. Yes, it is the same issue that tuples have always had (fields are typed but not named) but it would be nice to avoid if possible.

        Is there a reason why multi-declarations couldn’t depend on the target variable name, eg

        val (firstName, lastName) = getUser()

        could be equivalent to this?

        val tmp = getUser()
        val firstName = tmp.getFirstName()
        val lastName = tmp.getLastName()

        This would remove the need for generating component functions too..

        It would make the map case ugly though; it would result in:

        for((key,value) in counts)

        which isn’t what is wanted. Maybe also allow renaming/aliasing like this?

        for((user=key, count=value) in counts)
        // implicitly, user = entry.getKey(); count=entry.getValue()

        or perhaps there is some way to use annotations instead of special alias=property syntax?

        • I see what you are concerned about, but I don’t know any better solution than what we have: the renaming approach looks ugly…

          • Alexander Kosenkov says:

            Just force users to read all the components in case of ordered read

            data class User(first, middle, last)

            val (first, last) = getUser() // does not compile
            val (first, *, last) = getUser() // not interested in middle name
            val (last, *, first) = getUser() // compiles, but may issue a warning in IDE

            To implement this, compiler may simply check that there is no method called .component4

          • And then if someone adds an extension component4(), it will break with no way to fix it

          • Alexander Kosenkov says:

            That’s fine in my opinion. This is exactly why we use compilers!

            // Not interested in Last name and Age
            val (first, middle, *, *) = getUser()

            or even vararg-style, a bit ugly syntax:

            // Not interested in further evolution and hope for the best
            val (first, middle, **) = getUser()

            // I want to be safe
            val u = getUser()
            println “${u.first}, ${u.middle}”

  14. Alexander Shabanov says:

    What about nested bindings? Will they work too, e.g. assuming we have classes Foo, Bar and Baz defined as follows:
    class Foo { Bar getBar() {…} }
    class Bar { int getCount() {…} Baz getBaz() {…} }
    class Baz { String getName(); }

    is it possible to bind non-null fields by using the following code? -

    val ( bar ( count, baz ( name ) ) ) = myFoo;
    // bar, baz, count and name values now accessible

    Assuming bar field can be null in Foo object, is it possible to have “conditional” bindings, like follows:

    if-val ( bar ( count, baz ( name ) ) ) = myFoo {

    }

  15. Simon says:

    What are the implications for code optimisation?

    Ideally, something like this would not require any objects to be created:
    fun createPair() : Tuple2 { return (1,2) }
    val (a,b) = createPair()

    In this case, the compiler can see that no reference is held to the returned object, so is free to not actually create one, and simply pass the return values back in some other way. [1]

    When returning a user-defined object, isn’t this trickier?
    fun createPair() : PairOfInts { return PairOfInts(1,2) }
    val(a,b) = createPair()
    In particular, PairOfInts might have side-effects in its constructor or its component methods, making it unsafe to use alternate return paths. This isn’t an issue for a Tuple AFAICS (presumably Tuple classes are final).

    [1] Yes, some callers *might* hold on to the returned object directly rather than deconstructing it. But this could be worked around by creating two variants, one of which really creates an object, and an optimised one that returns two values directly. Also, I’m not sure if the JVM supports some way of returning multiple values – but can’t the function be transparently modified to take two output references as extra params or similar?

  16. Christian says:

    Have you thought about adding listener support for mutable fields in data classes?
    What about data binding? And even greater would be to be able to replace/extend the implementation of the generated listener/binding functionality.

    • Listeners/binding functionality is definitely on the table.
      I think this kind of thing should be handled by a different annotation, rather than putting everything in the world under “data”.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">