...

unscaled

2405

Karma

2015-01-07

Created

Recent Activity

  • Most people in Japan live outside of the Yamanote circle in Tokyo. Rural and Suburban supermarkets have parking lots (although in central areas they can still be quite small) and people still use cars for shopping trips, especially in the countryside.

    It is true that grocery packages are much smaller than the US (since Japanese houses, even in the countryside, are smaller and I guess the average household size is smaller as wel). Shopping carts in regular supermarkets are smaller than abroad, and are usually built to house 1 or 2 shopping baskets you can also carry by hand.

    But hey, we still have Costco in Japan, and package sizes and shopping cart sizes are just as big as they are in the US (although the parking lot is probably considerably more crowded). And Costco is extremely popular here. It's far messier than a Japanese supermarket and I do see inconsiderate people sometimes in Costco, but the cars are still parked nicely and most people do return their shopping carts. It would be interesting to compare Costcos in Japan and the US directly though.

  • I'm afraid this article kinda fails at at its job. It starts out with a very bold claim ("Zig is not only a new programming language, but it’s a totally new way to write programs"), but ends up listing a bunch of features that are not unique to Zig or even introduced by Zig: type inference (Invented in the late 60s, first practically implemented in the 80s), anonymous structs (C#, Go, Typescript, many ML-style languages), labeled breaks, functions that are not globally public by default...

    It seems like this is written from the perspective of C/C++ and Java and perhaps a couple of traditional (dynamically typed) languages.

    On the other hand, the concept that makes Zig really unique (comptime) is not touched upon at all. I would argue compile-time evaluation is not entirely new (you can look at Lisp macros back in the 60s), but the way Zig implements this feature and how it is used instead of generics is interesting enough to make Zig unique. I still feel like the claim is a bit hyperbolic, but there is a story that you can sell about Zig being unique. I wanted to read this story, but I feel like this is not it.

  • This is a pretty in depth overview of a complex topic, which unfortunately most people tends to dumb down considerably. Commonly cited articles such as "What Color is Your Function?" or Revisiting Coroutines by the de Moura and Ierusalimschy are insightful, but they tend to pick on a a subset of the properties that make up this complex topic of concurrency. Misguided commentators on HN often recommends these articles as reviews, but they are not reviews and you are guaranteed to learn all the wrong lessons if you approach them this way.

    This article looks like a real review. I only have one concern with it: It oversells M:N concurrency with green threads over async/await. If I understand correctly, it claims that async/await (as implemented by Rust, Python C# and Kotlin - not JavaScript) is less efficient (both in terms of RAM and CPU) than M:N concurrency using green threads. The main advantages it has is that No GC is required, C library calls carry no extra cost and the cost of using async functions is always explicit. This makes async/await great for a systems language like Rust, but it also pushes a hidden claim that Python, C# and Kotlin all made a mistake by choosing async/await. It's a more nuanced approach than what people take by incorrectly reading the articles I mentioned above, but I think it's still misguided. I might also be reading this incorrectly, but then I think the article is just not being clear enough about the issues of cost.

    To put it shortly: Both green threads and async/await are significantly costlier than single-threaded code, but their cost manifests in different ways. With async/await the cost mostly manifests at "suspension points" (whenever you're writing "await"), which are very explicit. With green threads, the cost is spread everywhere. The CPU cost of green threads includes not only the wrapping C library calls (which is mentioned), but also the cost of resizing or segmenting the stack (since we cannot juts preallocate a 1MiB stack for each coroutine). Go started out with segmented stacks and moved on to allocating a new small stack (2KiB IIRC) for each new goroutine and copying it to a new stack every time it needs to grow[1]. That mechanism alone carries its own overhead.

    The other issue that is mentioned with regards to async/await but is portrayed as "resolved" for green threads is memory efficiency, but this couldn't be farther from the truth: when it's implemented as a state machine, async/await is always more efficient than green threads. Async/await allocates memory on every suspension, but it only saves the state that needs to be saved for this suspension (as an oversimplification we can say it only saves the variables already allocated on the stack). Green threads, on the other hand, always allocate extra space on the stack, so there would always be some overhead. Don't get me wrong here: green threads with dynamic stacks are considerably cheaper than real threads and you can comfortably run hundreds of thousands of them on a single machine. But async/await state machines are even cheaper.

    I also have a few other nitpicks (maybe these issues come from the languages this article focuses on, mainly Go, Python, Rust and JavaScript)

    - If I understand correctly, the article claims async/await doesn't suffer from "multi-threading risks". This is mostly true in Rust, Python with GIL and JavaScript, for different reasons that have more to do with each language than async/await: JavaScript is single-threaded, Python (by default) has a GIL, and Rust doesn't let you have write non-thread-safe code even if you're using plain old threads. But that's not the case with C# or Kotlin: you still need to be careful with async/await in these languages just as you would be when writing goroutines in Go. On the other hand, if you write Lua coroutines (which are equivalent to Goroutines in Go), you can safely ignore synchronization unless you have a shared memory value that needs to be updated across suspension points.

    - Most green thread implementations would block the host thread completely if you call a blocking function from a non-blocking coroutine. Go is an outlier even among the languages that employ green threads, since it supports full preemption of long-running goroutines (even if no C library code is called). But even Go only added full support for preemption with Go 1.14. I'm not quite since when long-running Cgo function calls have been preemptible, but this still shows that Go is doing its own thing here. If you have to use green threads on another language like Lua or Erlang, you shouldn't expect this behavior.

    [1] https://blog.cloudflare.com/how-stacks-are-handled-in-go/

  • ArrowKt is also worth a mention: https://arrow-kt.io/learn/typed-errors/

  • I wish I could upvote this more. I can totally understand GP's sentiment, but we need to dispel the myth that results are just checked exceptions with better PR.

    I think the first issue is the most important one, and this is not just an implementation issue. Java eschewed generics on its first few versions. This is understandable, because generics were quite a new concept back then, with the only mainstream languages implementing them then being Ada and C++ - and the C++ implementation was brand new (in 1991), and quite problematic - it wouldn't work for Java. That being said, this was a mistake in hindsight, and it contributed to a lot of pain down the road. In this case, Java wanted to have exception safety, but the only way they could implement this was as another language feature that cannot interact with anything else.

    Without the composability provided by the type system, dealing with checked exceptions was always a pain, so most Java programmers just ended up wrapping them with runtime exceptions. Using checked exceptions "correctly" meant extremely verbose error handling with a crushing signal-to-noise ratio. Rust just does this more ergonomically (especially with crates like anyhow and thiserror).

HackerNews