Hacker News new | past | comments | ask | show | jobs | submit login

I'm not being unfair when I enumerate the most popular languages for comparison. When people crap on Javascript, they presumably prefer another language. And C#/Kotlin aren't exactly the top picks.

Kotlin has BYOB coroutines which are hard to work with. People don't use them. Going with the C# approach where async behavior looks sync was a bad move. I predict Kotlin's coroutines will never be a centerpiece abstraction just like how people don't really use Go's channels (people in practice just go back to Mutexes).

I mean, try it. Write the equivalent to this in Kotlin:

    // get background work started now
    const background = promise()
    
    // crawl some urls concurrently as well, just 4 at a time
    const crawl = Promise.map(urls, crawl, { concurrency: 4 })

    // while that's going on, we have some work that
    // we must get done.
    for (const task of tasks) {
      await worker(task)
    }

    // worker's done, now we can wait on 
    // the crawler and background work.
    const [a, b] = await Promise.all([
      crawl.then(processResults),
      background
    ])
CSP never caught on because after you have more than one channel as a central bus (the toy architecture), you immediately descend into channel hell. In-channels, out-channels, channels over channels. Back to using pencil and paper and scouring your code to decode the classic buffer bloat problem.

A single-threaded event loop with a central promise abstraction is a great way to write networked code.

Btw, I use Kotlin in a large JVM project and I'm stuck with the horror of https://docs.oracle.com/javase/8/docs/api/java/util/concurre.... That's more likely what you'll be doing day to day with Kotlin, not playing with its toy coroutines.




Whats wrong with something like this

  import kotlinx.coroutines.*
  import kotlinx.coroutines.channels.Channel

  fun background(): Channel<String> {
      val ch = Channel<String>()
      GlobalScope.launch {
          delay(5000)
          ch.send("OK")
      }
      return ch
  }

  fun getWebsiteData(url: String): Channel<String> {
      val ch = Channel<String>()
      GlobalScope.launch {
          delay(4000)
          ch.send("website data")
      }
      return ch
  }

  fun work(): Channel<String> {
      val ch = Channel<String>()
      GlobalScope.launch {
          delay(1000)
          ch.send("work result")
      }
      return ch
  }

  fun main() = runBlocking {
      println("START")
      val bck = background()
      val urls = listOf("www.web1.com", "www.web2.com", "www.web3.com", "www.web4.com")
      val chs = urls.map { getWebsiteData(it) }
      generateSequence { work() }.take(4).forEach {
          println("sync job done: " + it.receive())
      }
      chs.forEach {
          println("data fetched done: " + it.receive())
      }
      println("background done:" + bck.receive())
      println("END")
  }


Re:Kotlin, I'm not a JVM expert/fan, so I'll take your word for that. As for the lower-level coroutining, isn't the point that this will allow you/library-writers to abstract over these and provide the higher-level abstractions that you want to use?

Regarding "C# approach where async behavior looks sync", I don't follow you here. C# is very explict with async's returning Task<> where with Go's lack of "colored" fns, for example, you don't really know when you code is async or not.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: