Hacker News new | ask | show | jobs
by poikroequ 652 days ago
I think both languages have their strengths. I love Kotlin for its functional programming (map, filter, etc) and strong static typing. But Python has some nice features as well, such as list comprehension, the 'yield' keyword, and annotations are super simple to implement.
3 comments

Are there things that you find easier to express in a list comprehension format than you do with Kotlin's standard library? I've always found comprehensions to be a bit of a workaround to the fact that Python doesn't have great support for anonymous functions, and I've never found a situation where I'm writing Kotlin really wishing I had a comprehension.
A bit of a contrived example, but something like this (two for statements):

[x*y for x in range (10) for y in range(10)]

It's not often, but occasionally there are moments where I'm writing code in Kotlin and wish I could use a list comprehension. I do prefer Kotlin overall, but there's a few things that I think would be "nice to have" from Python. Especially the yield keyword, such a wonderful way to write your own iterators.

Like this?

  sequence { for (x in 0..<10) for (y in 0..<10) yield(x*y) }.toList()
Now, technically, Kotlin doesn't have list comprehensions, only the equivalent of generator expressions in Python, so you have to tack an extra `.toList()` on at the end if you want a list, but you can write pretty much any for comprehension in Python in a similar way in Kotlin.

On the other hand, you're not limited to for loops/ifs inside such a generator, but can use fairly arbitrary control flow.

You could also build the list directly.

  buildList { for (x in 0..<10) for (y in 0..<10) add(x*y) }
Hmm, I actually prefer Kotlin's version, but maybe that's just because it's what I'm used to:

  (0..10).flatMap { x -> (0..10).map { y -> x * y } }
The flow of the data is more intuitive for me because you don't use variables before they're defined.

> Especially the yield keyword, such a wonderful way to write your own iterators.

Maybe I'm missing something about `yield` in Python—can it do something that Kotlin generators can't?

It's mostly ease of semantics -- in your example you use two layers of map and as a result need to do flatMap instead of just map twice

In py, for list/set/dictionary/generator comprehensions, the format is always the same and always the same as if you were to do it as a normal nested loop, save for the statement being first instead of last (you can also do filters using normal if statement syntax, these go at the end/after all your loops).

I actually like statement first because it gets to the "meat" of the semantics before the context (which loop etc), but end do the day it's all a bit arbitrary

@ yield, there's literally no difference between Python and kotlin. Python also offers a generator comprehension, which is nice, but it has nothing to do with yield

i_am_a_generator = ( x+1 for x in range(10*100) )

Theres the zip function builtin, which I actually would have preferred

  oneToTen.zip(oneToTen2) { x, y -> x * y }
That's not actually equivalent. The initial version they included produces:

  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 ...]
Whereas yours is just:

  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
I’ve always liked that the structure of list comprehensions mirror SQL with “SELECT, FROM, WHERE” sections.

But I know that I’m in the minority that likes SQL

They remind me of C#'s LINQ
> the 'yield' keyword

Am I missing something here?

  $ cat fib.kts
  fun fib() = sequence {
    var a = 1; var b = 1
    while (true) {
      yield(a)
      a = b.also { b += a }
    }
  }

  println(fib().take(10).joinToString(" -> "))
  println(fib().first { it >= 50 })
  $ kotlin fib.kts
  1 -> 1 -> 2 -> 3 -> 5 -> 8 -> 13 -> 21 -> 34 -> 55
  55
Of course, yield() is a function in Kotlin, not a keyword, but the same functionality is there.
The mechanism in Kotlin that allows them to limit yield() to a sequence{} block , without introducing a new keyword, is pretty dang cool.
What happens under the hood is that a `sequence {}` call creates an instance of `SequenceScope`, which has `yield()` and `yieldAll()` methods. When executing the block, `this` will reference that particular instance and `yield()` is essentially `this.yield()` and will call the method on the scope instance.

The actual functionality is then provided by the coroutine system, though a lot of the heavy lifting is done by the optimizer to eliminate all or most of the runtime overhead.

I had no idea Kotlin could do this. I never thought to check. I guess with Kotlin being a JVM language, I simply assumed it wasn't possible.

Thank you for providing an example because I was a little confused by a couple other comments mentioning yield...

From your other comment, I'm curious what you like about yield and why you think Kotlin is lacking without it.