There's a difference between "running a task that waits for 10 seconds" and "scheduling a wakeup in 10 seconds".
The code for several of the languages that are low-memory usage that do the second while the high memory usage results do the first. For example, on my machine the article's go code uses 2.5GB of memory but the following code uses only 124MB. That difference is in-line with the rust results.
package main
import (
"os"
"strconv"
"sync"
"time"
)
func main() {
numRoutines, _ := strconv.Atoi(os.Args[1])
var wg sync.WaitGroup
for i := 0; i < numRoutines; i++ {
wg.Add(1)
time.AfterFunc(10*time.Second, wg.Done)
}
wg.Wait()
}
Consider ordering reusable chunks of your circuit as PCBs instead of iterating on entire boards. You can connect the chunks with breadboards or connectors or something.
https://github.com/augustoroman/sandwich is another approach for achieving a similar result. One nice aspect of writing handlers that return errors is that they are easy to test and clean to write.
Sandwich does very simple sequencing to achieve an understandable dependency injection mechanism that produces clear errors when something goes wrong, and it produces errors at setup time rather than run time.
It was an interesting mix of "let's do a cool thing" and "imagine what can be done with this data!" Early on someone mentioned that NYC doesn't actually know for sure where all of the fire hydrants are _actually_ placed, and it may be possible to automatically extract the locations from the collected data.
More obviously, looking for the address signs of addresses that are expected along a street can dramatically improve driving directions.
Of course, the biggest business value was being able to generate the actual, underlying street maps without having to purchase that from companies that had already digitized and driven the streets.
The complexity from generics isn't from the conceptual standpoint, it's from the resulting code standpoint... for the same reason that the ternary ?: operator is "too complex": it's really easy to say that `foo := cond ? 1 : 0` is better than the if/else alternative, but `foo := (a > (b.Active() ? Compare(b, a) : useDefault() ? DEFAULT : panic()) ? "worked" : "failed"` is a mess waiting to happen.
Same with generics. It's easy to point to simple examples. It hard to ensure that terrible metaprogramming monstrosities don't arise.
It's possible to write bad code with go as it is, but it's actually difficult to make such bad code inscrutable.
The code for several of the languages that are low-memory usage that do the second while the high memory usage results do the first. For example, on my machine the article's go code uses 2.5GB of memory but the following code uses only 124MB. That difference is in-line with the rust results.