The pricing was already getting untenable for many workloads when Salesforce took over (AFAICT they basically never dropped their price / compute as everyone else did), but the shift from “best dev experience available” to “handy way to build salesforce apps” really ran it into the ground.
For these, we must instead decompose into smaller tasks. Providing an intermediate representation that can be stored (again, not locally!) somewhere and loaded, while keeping track of where in the sequence we are to resume processing after cancellation.
https://github.com/shopify/job-iteration Shopify has a gem precisely for that. And even have another gem thar gives a UI to run one-off tasks that one often needs to run that takes advantage of the ‘resumability’
Slight OT: is there guidance on how to deploy an application that uses sqlite. Do you need to rely on a smart load balancer to guarantee no-downtime deployments?
You can use systemd socket activation or s similar socket handoff technique. The idea is another process opens the sockets and hands them off to your service. When the service reloads it takes them back, then hands them to the next instance. There is a slight delay but no dropped connections.
One thing I realized while thinking about this is it’s only the write requests you need to worry about. If you have a load balancer you can route all the GET requests to a separate process. That process can safely do rolling restarts or staged rollouts.
Honestly if you want to guarantee no-downtime deployments, you will suffer a lot less just using postgres or whatever SQL flavor your provider likely offers, even in unmanaged form. You can get the experience of “my database is a single file to backup” by calling sqldump > backup.sql and taking that.
Sqlite is nice if you want low ops and don’t have that many durability requirements but if you’re at downtime prevention you’re going to have to do a bunch of work anyways.
Hi everyone. I’m launching Linguist Translate, an open-source, full-featured translation solution with an embedded offline translator based on the Bergamot Project created by Mozilla.
Linguist is not just a wrapper over Google Translator like many other extensions. You can use any translation service with Linguist, thanks to custom translators! You may even deploy any machine translation (like LibreTranslate) on your localhost and then add this service to Linguist.
All features are included: text translation, full-page translation, selected text translation, Text-To-Speech, dictionary, history, and even more.
I just installed it and the default was Google Translate?
You headline here is “privacy focussed offline translation”, which seemed to imply that it was using Bergamot by default. ☹️
Just pick Bergamot in preferences. Privacy is not about “trust by default”.
A Bergamot Translator is not the only feature about privacy in Linguist. Linguist protect your privacy by design and give you abilities that is unreachable in another extensions. Check a custom translators in Linguist, for example. Do you know any other extension who can do like that?
I mean, can you deploy machine translation on you local machine and then use this service with another browser extension for translation? As i know, there is no such features in other addons. When i implemented this feature, people told be “it’s just madness”. But with Linguist you can do it.
With Linguist - any translation service or LLM in universe will be served for you as translators. Linguist features will be grow, but all features will be based on translator you prefer in options.
Currently we work on images translations. Do you know how it implemented in most solutions? Addons just sent images from a pages to remote server, then use google translation and return it to you. Linguist will recognize text locally, then use translator you prefer, to translate text, and then re-draw image locally. So you still may sent text from image to internet, but even if you will try to translate your WhatsApp messages with sensitive content - nobody will see your cat, but only text from image may be compromised. If you will use offline translator, then even your text will be private.
This is too long story to explain it on every press release, but this is why Linguist is private, its really care about your privacy unlike other translators who just make money on your data.
Frankly, I disagree. If your submission here is titled “privacy focused offline”, it shouldn’t default to an online service.
I like the feature set and it’s a true value proposition on top of what e.g., Firefox has as a built-in default, privacy-focused offline translation. Custom dictionaries, learning, history and lookup. That’s all really cool. But Linguist shouldn’t call itself what it’s not.
Linguist is not an “offline translator”, it is full-featured translation solution.
The title of current post is “offline translation in your browser” that is true, if you can do 3 clicks.
The headline features you advertised are things that the program can technically do, but which it won’t do by default because it makes it work worse.
“Offline” means offline. If the first thing your translator does is reach out to the internet to talk to Google Translate, it’s not offline. Call it “offline-capable” if you want, that’s about the right level of weasel-words. The explanation of it on the website is perfectly reasonable.
Why not make Bergamot the default? People can always choose to use Google, but as @freddyb pointed out, using Google by default clashes a bit with the valuable privacy angle.
Good question, actually. This is trade off between privacy and available languages. Bergamot translator currently have not as much languages as Google Translator have.
Most users don’t care about offline translation, so translation extension that can’t translate Japanese to Hebrew not so good. So default is Google translator which have a lot of languages. But users who need offline translation, may change translator to Bergamot in 3 clicks.
When Bergamot will have enough languages directions, we will change default to Bergamot.
I understand you here, i felt the same. Actually, I have created a Linguist when i changed Chrome to Firefox, because Firefox did not have embedded translator at all, and another extensions looks not enough good.
HTTP is the new FastCGI. Do shared hosts not support running persistent processes? If not, they should. File-based script hosting is dead, it’s not general enough and every app wants full control of when things happen.
Do shared hosts not support running persistent processes? If not, they should.
Given how many users are typically given accounts on a single host, this would be extremely wasteful for memory compared to CGI. Shared hosting (at least back in the day) was dramatically cheaper specifically because you could fit a lot more customers on a single machine.
it’s not general enough
It’s not general enough to replace a VPS, but it’s general enough for me. Just because it doesn’t solve every problem out there doesn’t mean it’s dead.
Thanks to systemd socket activation is actually trivial to have a http server that starts on demand and shuts down after some period of inactivity, effectively not wasting any ram “just to sit there”. Sorry for the twitter link, but if someone wants to follow I posted description recently: https://x.com/dpc_pw/status/1797540651229438147
This opens the way for hosting many SvelteKit apps on a single host with automatic shutdown and near instant startup.
The author of the above feature has a project that packages up SvelteKit apps, a Node.js executable and glibc++ (The total size of the final image is approximately 37 MB). This is deployed as a systemd ‘Portable Service’: https://github.com/karimfromjordan/sveltekit-systemd
Well, but keep in mind that there’s two “modes” of FastCGI:
Run a permanent process at a known address.
The webserver spawns semi-persistent processes at need, passes them an open socket, uses them as long as they seem busy and interested, then kills them at whim.
Option 1 is the most well-known, because it makes sense for php-fpm. But IMO option 2 is the more interesting one, because it gets you the performance benefits of a persistent process while preserving a shared host’s ability to keep prices low by over-subscribing a server where many of the hosted sites and apps have very low-traffic.
Personally, I want option 2 but for HTTP — call it a “reverse-proxy scheduler” model. Use config to mount a small http-speaking app at a particular path in your site’s URL hierarchy, and let the main web server spawn/kill it as it sees fit. This doesn’t exist, but IMO it should; and glancing around, it doesn’t seem like I’m the only one who wants this.
The webserver spawns semi-persistent processes at need, passes them an open socket, uses them as long as they seem busy and interested, then kills them at whim.
Isn’t that how Heroku worked? They would run your HTTP-speaking application in a container, which AFAIK would be killed off if idle for a while. Also I think a lot of these modern cloud offerings like AWS and Azure have something that works in a similar way. I think the main problem with these things is that it’s a bit heavier-weight and more expensive. Sounds very much like your “reverse-proxy scheduler”
Yeah so I was going to “invent” AGI (Andy’s Gateway Interface) – which is just any HTTP server with FastCGI-style process management …
AGI_LISTEN_PORT=9090 # server sets this, and the binary listens on that port
(could also be Unix domain socket path)
AGI_CONCURRENCY=2 # for threads or goroutines perhaps
The key points are
PHP-like deployment. I drop a file in and it runs …
Your process can be killed, made dormant, and restarted at any time. This is necessary for shared hosting to be economical. 99% of apps have like 100 hits or less a day. You don’t want to take up server memory keeping them alive. This is precisely the “cold start” issue in the cloud, which has a rationale
This does not exist currently, but there’s no reason it shouldn’t!
Most shared hosts don’t support persistent processes. Dreamhost doesn’t.
Mythic Beasts does, but somewhat surprisingly it exposes systemd config files in your home dir:
Like basically using cron jobs to restart them … this seems like “non-support”.
My guess that it creates more of a server management headache. But it is something that the cloud providers have solved, and it gives a better user experience.
Mythic Beasts does, but somewhat surprisingly it exposes systemd config files in your home dir:
Systemd actually provides most of a solution here. Use socket activation to start your daemon when a connection comes in and then your daemon can exit itself after some idle time. If another tcp connection comes in, Systemd will reuse the connection to the daemon while it still exists and start the daemon again if it has exited.
Yeah I’m definitely going to kick the tires on this … Last week, I set up uswgi on OpalStack, and I also wanted to try the recommended Python thing on Mythic Beasts
The degree of outrage was surprising; I sympathize with the maintainer here.
I’m not sure I would have taken too much notice of it. There is a ton of negative sentiment from tech people, particularly on the orange site. Best to ignore most of it and focus on people who have something constructive to offer.
I usually agree with hindsight being 20/20 but adding AI to a terminal application automatically on a minor software upgrade was not going be received well. Same would have applied to crypto…
It didn’t add it automatically. You had to complete several steps including providing your own API key for it to start working on your explicit request.
I am the first to object to random AI integration, have no use for this feature, and also have other reasons why I don’t and probably will never again use iTerm2. All of that said:
Although the feature was there in the core, it didn’t work out of the box. You needed to explicitly configure it to use an API key that you provided. I am quite surprised that it would be problematic for people.
Weird take. Obviously you could do everything in untyped lambda calculus, but that sucks. Likewise generics significantly reduce the need to use the any type, eliminating most remaining untype checked code. Adoption is low because it’s used with restraint.
Likewise generics significantly reduce the need to use the any type, eliminating most remaining untype checked code
There’s absolutely zero doubt that generics are useful. Literally everyone agrees with that!
What is also true is that generics have costs, across many dimensions. I think it’s also true that literally everyone agrees that generics are non-free.
What is curious though, is that it seems that there’s a sizable population who don’t acknowledge that cost. Which looks like:
Person A: generics are useful for X but they have costs C.
Person B: no, generics are useful for Y.
Another observation here is that the overall type system complexity is a spectrum. And this tradeoff-vs-zero cost dialog tends to repeat at any point on this spectrum during the language evolution.
Taking Rust as an example, I think the complexity of the trait system (type classes with type projections, including type function projections) is necessary complexity to make Rust’s attempt at safe, “zero-cost” concurrency viable. On the other hand, I can’t deny that some of the way I see the trait system used in the ecosystem (and even, unfortunately, in certain recent, dubious, as-yet-unstable additions the standard library) is awful library design and obviously the sophomoric obsession with “expressive type level programming” that Don Syme alludes to. I try to avoid depending on crates like this as much as I can, and my own code rarely ever involves defining new traits.
But how can we tame this jungle of expressiveness without losing the ability to prevent classes of bugs via automated, formal program analysis? One option perhaps is to have a clear distinction between the “application language” and the “systems language,” allowing you to omit features in each and create a simpler language in each category, whose features together combine via sum (application language + systems language) instead of product (application features * systems features).
Baring that, I would say that there is always the informal technique of culture development so as to avoid the use of terrible type programming. In other words, I want a language which can expression typenum, with a community of practitioners who are wise enough never to actually use typenum. I don’t see why that should be beyond us.
But how can we tame this jungle of expressiveness without losing the ability to prevent classes of bugs via automated, formal program analysis?
My gut feeling is that there’s still room for improvement language design wise, that we are still pretty far from expressiveness-complexity Pareto frontier.
That’s very unsubstantiated feeling, but two specific observations make me feel that way:
module-type class conflicts. Modules feel simultaneously more composable and simpler, while traits win on ergonomics. It feels like there should be some way to have both.
the way that Zig does generics, in the sense of convincing the compiler to generate the code you want, is refreshingly simple. But of course it doesn’t allow for automated modular reasoning. It feels like there should be some way to regain the modularity without adding a Turing tarpit type-level language.
One possible mitigation is culture and idiom. Maintainers have a lot of soft power here. They write the reference documentation that people read when onboarding to the language. Some slogan about “concrete is better than abstract” could do a lot of work (it would probably be invoked legalistically in many cases, but that seems like the lesser evil to me). I think culture does a lot of work to keep Go code simple even since generics have been released.
I can somewhat easily envision someone writing Rust closer to how they would write C. I could even see the Linux project implementing guidelines for Rust that aim to keep it simple, and maybe other projects adopt those guidelines. Maybe they get enough traction to influence Rust culture more broadly?
One possible mitigation is culture and idiom. Maintainers have a lot of soft power here.
We could probably do a better job with the language we use to describe some features. For example, calling a feature advanced implies that it’s users are not beginners. If you instead call a feature niche, you imply that (1) it is not meant to be commonly used and (2) people do not need to understand it to be productive.
I think it’s also true that literally everyone agrees that generics are non-free. What is curious though, is that it seems that there’s a sizable population who don’t acknowledge that cost.
I’m confused about the distinction between “everyone agrees that generics are non-free” and “there’s a sizable population who don’t acknowledge that cost”. If there are people who don’t acknowledge the cost, doesn’t that imply disagreement that generics are non-free?
Aside from that confusion, I agree. In many of the debates I’ve witnessed about Go and generics over the years, the rabidly anti-Go people (who have always been extremely numerous, i.e., I’m not exactly nut-picking) have typically refused to concede any downside to generics whatsoever. Similarly, they’ve refused to acknowledge any utility in standardization (“most Go code is written in a very similar way”). And in fairness, I didn’t really think much of the standardization property before I used Go in earnest either, but then again I didn’t go around arguing decisively against it as though I had some kind of expertise with or perfect knowledge about it.
By contrast, within the Go community there has usually been a pretty robust debate about costs and benefits. Marginally few people would argue that generics confer no benefit whatsoever, for example.
I might be using wrong words here, so let me make this more specific. Let’s take a simple statement “generics make compiler harder to implement”. I can see people expressing the following positions:
no, generics don’t actually make compiler harder to implement (disagreement that there’s a cost)
generics make the compiler just a tiny bit more complex (disagreement about the magnitude of the cost)
generics make the compiler more complicated, but you need to write the compiler once and you use generics daily (statement about tradeoff)
generics make the compiler more complicated, but that’s irrelevant so I won’t be even addressing that (knowing, but not acknowledging the cost).
From my observation of such discussions, I think position 4 is puzzlingly prevalent. Well, maybe I am misunderstanding thing and that’s actually position 1, but I find that unlikely. But also that’s squishi human theory-of-mind stuff, so I might be very wrong here.
I think part of the disconnect here is that apparently you’re very concerned about complexity in the compiler. I didn’t realise that was your concern until this comment.
Obviously a slow, buggy compiler is bad, but I generally view the role of the compiler as eating complexity off the plate of the programmer; instead the costs I’m thinking about are those that fall on the users of compilers.
No, I am not very concerned about implementation complexity. I use this as the first example because:
this is something I am unusually familiar with
this is the most clear cut and objective cost, which is important for the meta discussion of ignoring costs
As I’ve written in my other comment(and, before that, at https://matklad.github.io/2021/02/24/another-generic-dilemma.html), my central concern is the ecosystem cost in the form of more-complex-than-it-needs-to-be patterns and libraries. And, to repeat, Go is an interesting case here because it is uniquely positioned to push back exactly against this kind of pressure.
One cost to the “generic” generics I am very well aware of. I was at JetBrains when support for Rust and Go was added to the platform, what was later to become GoLand and RustRover.
For Go, the story was that the language semantics was just done at one point, and devs continued to hack on actual IDE features. For Rust, well, we are still trying to cover the entire language! I don’t think either rust-analyzer or RustRover support all type system features at the moment?
Now, current Go generics are much simpler than what Rust had circa 2015, so the situation is by far not that bad. But this is still a big increase in implementation complexity relative to what’s been there before.
What I think is the biggest cost is the effect on the ecosystem. You can’t actually stick to first-order code, because the libraries you use won’t! There’s definitely a tendency to push ecosystem towards complexity, which plays out in Rust for example. See, e.g., boats comment. But here I can confidently say you don’t need to go all the way up to Rust to get you into trouble — I’ve definitely lost some time to unnecessarily generic Java 7. I am very curious how this plays out in Go though! Go has very strong focus on simplicity, so perhaps they’ll reign this social tendency in?
Another cost of generics is compile time or runtime. Generics are either slow to compile, or slow to run. This is also often a cost imposed on you by your dependencies.
Finally, there’s this whole expressively treadmill: languages start with simple generics, but then the supports tends to grow until past the breaking point. Two prominent examples here are Java type system becoming unsound without anyone noticing, and Swift type system growing until it could encode undecidable word-equivalence in a semi-group problem.
Now, current Go generics are much simpler than what Rust had circa 2015, so the situation is by far not that bad. But this is still a big increase in implementation complexity relative to what’s been there before.
Per your other comment, I’m in camp 3: it’s a one time cost, and unless it takes literal years to pay it, we can mostly discount it: the compiler team is tiny compared to the size of the whole community, so the only relevant cost I see here is opportunity cost: what else could the compiler team do instead of implementing generics, that would make life even better for everyone else?
Another cost of generics is compile time or runtime. Generics are either slow to compile, or slow to run.
I believe Ocaml disproved that a number of decades ago.
Its solution required a small compromise (to avoid heap allocating integers, their range is cut in half), but the result is dead simple: generic code can copy integers/pointers around, and that’s about it. Compile once, run on any type. And when the GC comes in, discriminating between integers and pointers is easy: integers are odd, pointers are even. The result is fast to compile and run, as well as fairly simple to implement. And if we needs natively sized integers, we can still heap allocate them (the standard library has explicit support for such).
Oh but you’d have to think of that up front, and it’s pretty clear to me the original Go devs didn’t. I mean I have implemented a statically typed scripting language myself, and I can tell from experience: generics aren’t that hard to implement. The Go team would have known that if they tried.
Finally, there’s this whole expressively treadmill: languages start with simple generics, but then the supports tends to grow until past the breaking point.
This slippery slope argument also applies to languages that don’t have generics to begin with. See Go itself for instance. The problem doesn’t come from generics, it comes from an unclear or expanding scope. If you want to avoid such bloat, you need to address a specific niche, make sure you address it well, and then promote your language for that niche only.
I’m pretty sure Go acquired generics for one of two reasons: either it wasn’t addressing the niche it was supposed to address well enough, and the compiler team had to say “oops” and add them; or people started using it outside of its intended niche, and the compiler team felt compelled to support those new use cases.
I believe Ocaml disproved that a number of decades ago.
Not really, Ocaml uses standard solution in the “slow code” corner of design space: everything is a pointer (with an important carve out for ints, floats, and other primitive types).
This is the case where generics make even non-generic code slower.
That’s fair, but I still have a trick up my sleeve: there’s an intermediate solution between full monomorphisation and dispatch/everything-is-a-pointer. In C++ for instance, most template instantiations differ by one thing and one thing alone: sizeof().
Which narrows the choice quite a bit. Now we’re choosing between compiling once per type size, and passing an additional parameter to the generic functions (the type size). Sure you won’t get the fancy stuff like copy constructors, but the compilation & runtime costs involved here are much, much tamer than the classic solutions.
It actually does, thanks for the link. And from the look of it, it does sound like it’s more complicated than just sizeof(), which I would expect if you want to support stuff like non-shallow generic copies. I’ll look it up.
I don’t think that’s true. That’s true of much more complex higher order type systems than go’s generics.
I think it is true of any possible implementation of generics. The two implementation choices are dynamic dispatch or monomorphisation. Dynamic dispatch includes a run-time cost, which may be small but also adds a bit more in terms of impeding inlining. Monomorphisation incurs a compile-time cost because you have to create multiple copies of the function.
Can you expand on that? I’m guessing that you mean that a program expressed without generics but solving the same problem will require more parsing and may end up being slower to compile?
Sorry, I just meant that even though monomorphisation takes non-zero time it doesn’t follow that overall compilation will be even perceptibly slower. Not free but maybe still cheap.
Monomorphisation incurs a compile-time cost because you have to create multiple copies of the function.
That and maybe some optimizations (during compile) can be done when the compiler is smart about generics rather than not knowing that similar things are actually the same.
The problem with C++ / Rust style generics is that monomorphized functions are generated at the use site, where the concrete types are known. They create lots of duplicate copies of monomorphized functions that must subsequently be deduplicated.
This is more of a problem for C++ than Rust, because C++ does this per compilation unit and so the linker typically throws away around 90% of the generated object code in the final link step. Rust doesn’t have the same separate compilation model, but still suffers from some of the same cases where the generated functions are identical and you need to merge them.
Or you do dynamic dispatch and have one copy of the function that can do the same work, just without any specialisation.
The tricky thing for a compiler (and I don’t know of any that are good at this) is working out when monomorphisation is a good idea. Both Rust and C# (and some ML dialects) have attributes that let you control whether something should be monomorphised. Go uses an approach where they monomorphise based on the size of types but do dynamic dispatch for method calls.
If I recall correctly OCaml does neither monomorphisation nor dynamic dispatch. With the possible exception of special cases like structural equality, generic code simply treat generic data as opaque pointers, and as such runs exactly as fast as a monomorphic version would have.
The GC does some dynamic dispatch to distinguish integers from pointers, and integer arithmetic does do some gymnastic to ignore the least significant bit (set to 1 to distinguish them from pointers), so the cost is not nil. As high as actual dynamic dispatch though? I have my doubts.
Time taken directly: 50.722233ms
Time taken using interface: 57.640016ms
Time taken using generics: 143.882627ms
There’s just more indirection with generics, relative to interface passing. And the alternative to generics is not always an interface — often it is “this code doesn’t actually need to be generic”. And at that point, the difference might be stark: either you don’t monomorphise, in which case the non-generic code, by virtue of inlining, becomes massively faster, or you monomorphise, and now your thing compiles many times slower.
You’re talking about a language that added generics after the fact. This adds constraints they likely wouldn’t have had if they did it from the start, and is much more likely to bias heavily against generics. Try the same with OCaml, I bet the results would be very different.
If you don’t have generics, most of the time, rather than using any, you refactor the code to not be generic. In any case, any is not slow:
func count_to_million_any(c interface{}) {
for i := 0; i < 100000000; i++ {
cc, _ := c.(*CounterImpl)
cc.increment()
}
}
Time taken directly: 42.200609ms
Time taken using interface: 34.227707ms
Time taken using generics: 143.833287ms
Time taken using any: 49.59427ms
This makes sense — it’s still static dispatch after the downcast, and the downcast should be speculated right through.
You do have to be careful about inlining and devirualization. In this case, the interface version is being inlined and devirtualized and so it ends up doing the exact same code as the direct version. Adding //go:noinline annotations to the four functions changes the results from (on my PC on Go 1.22.4)
Time taken directly: 21.522505ms
Time taken using interface: 21.692153ms
Time taken using generics: 129.865654ms
Time taken using any: 21.737464ms
to
Time taken directly: 21.559384ms
Time taken using interface: 128.452964ms
Time taken using generics: 128.384825ms
Time taken using any: 42.816321ms
which matches what I expected: generics are implemented using dictionary passing, so it’s a virtual dispatch call just like interfaces, and the any version is going to do slightly more work than the increment checking the type every loop.
People like to claim that Go isn’t a particularly smart compiler, but I find that it does do quite a bit of useful optimizations and instead chooses to skip out on those that are less bang for the buck. For example, in this case, it actually executes every loop and increments instead of just loading a constant like many C compilers would to (IMO) dubious benefit.
Typically the any type isn’t what we’d use in the absence of generics. I’ve written a lot of Go since 2012 and only a vanishingly small percentage of it uses any for generic use cases. Far more frequently, I just write type FooLinkedList struct { Foo Foo; Next *FooLinkedList } because it’s both more performant and more ergonomic than an any-based linked list with type assertions at the boundary.
I was using linked lists a lot when was writing code in C. It is interesting that I never used linked lists in Go during the last 12 years. Standard slices work exceptionally well in places where C would require linked lists. As a bonus, slices induce less overhead on Go garbage collector, since they are free from Next and Prev pointer chasing.
IMHO, slices is the best concept in Go. They are fast, they are universal, they allow re-using memory and saving memory allocations via a = a[:0] trick.
I agree. I don’t use linked lists very often, and my point wasn’t that linked lists are a particularly good container; only that in the rare instances when I need to do something generic, I’m only very rarely reaching for any. There’s almost always a simpler solution. Usually it’s the builtin generic types, but even when it’s not, there’s almost always a better solution than any.
In English, “you may have dumb coworkers” does not mean “all of your coworkers are dumb”. Moreover, Go doesn’t have any design principle with respect to “dumb coworkers”, but it does care about keeping the cognitive burden small (you don’t have to be “dumb” to make mistakes or to want to spend your cognitive budget on business rather than language problems). I don’t think that’s uniquely a concern that Go has expressed–every language which exists because C or C++ or JavaScript or etc are too error prone is implicitly expressing concern for cognitive burden. Everyone who argues that static type systems provide guard rails which make it more difficult for people to write certain kinds of bad code is making essentially the same argument.
The cost is that your dumb coworkers can have more ways to write horrible code.
You aren’t taking into account what’s the alternative to generics. People aren’t just going to stand idly and write the same function twelve times, they are going to bolt on macro systems (m4, cpp, fuck even jinja) on top of the source code, implement custom code preprocessors or parsers (pretty much every big C++ project of the 90s), use magic comments, funky build system extensions (you don’t know how far you can get with CMake and some regexes), etc. One way or another the written code is going to be generic… and I tend to think it’s much better if it’s in a language-sanctioned way rather than every project reinventing its own take on it. Today in C++ people do so much more things in-language than in weird external tools compared to 20 years ago, and that’s thanks to the continuous increase in expressive power of its type system.
Alternatively, people may think more and come up with simpler solution, which doesn’t require generics and external code generation. From my experience well-thought interface-based solutions in Go are easier to read and reason about than generic-based solutions.
Update: when talking about interfaces in Go, people frequently think about empty interfaces and forget about non-empty interfaces exposing the minimal set of functions needed for solving the given generic task.
My concern was (and sort of still is) that people would start writing Go libraries with gratuitous abstraction, like they do with other languages that embrace generics. “Just don’t use those libraries” isn’t much of a consolation if that’s the only available library for a given domain nor if the gratuitous abstraction becomes a property of a vast chunk of the ecosystem as with other languages. I’m happy to report that my concern has so far been misplaced–the Go community has done a fantastic job about exercising restraint.
Is that true? IIRC, if so, wouldn’t they have been added a long time ago, with less resistance both from the PL creators and the community? I’ve (unfortunately) only recent had to delve back into Go, after previously using it professionally a few years prior.
Yes. This is true. Every one agrees that generics are beneficial. Luckily, go language designers, besides knowing quite a lot about benefits of generics, are also keenly aware of the costs. The costs are the reason why this isn’t a no-brainier feature to add in 0.1 version of the language.
As my old hardware prof told me, even the most crappy CPU feature have uses! “It is useful” can’t be a reason to add something, you need to compare it to the costs.
I’m glad to see the “used with restraint”. I was worried (and still am concerned) that people were going to write gratuitously abstract libraries like we see in other languages. But so far I’ve been happy with the degree to which people have used them appropriately in the ecosystem.
Using S3 sounds like a great way to set yourself up to be financially trolled hard. There are lots of unlikable companies people hold grudges against. Many many companies use S3 nowadays even on their corporate websites via CMSes for image hosting etc.
Because pointers come with some overhead, the natural reaction is to avoid them at all costs and resort to passing struct value copies wherever possible.
I’m probably being pedantic, but pointers don’t really have “overhead”, at least not in the sense that most OOP languages have overhead for objects. A pointer in Go is exactly an address, no more no less. Dereferencing a pointer takes some time, but not necessarily more than the time it would take to pass a copy. The main issue with pointers is that you have to be careful about how you use them or else they will escape the stack resulting in an allocation which is expensive.
Unfortunately, Go doesn’t have very good tooling for flagging these allocations (that I know of, anyway). I would love for someone to build a VS Code extension that highlights allocations or a linter that lets me annotate a line or function with //go:noalloc and then yells loudly if that line/function starts to allocate.
I’d be curious to hear how this would work in a production setting. Having a single instance makes the current implementation consistent at the expense of being “HA”.
I believe it’s not as simple as keeping a “warm” second instance.
Yeah, I’ve been thinking about having shared-nothing instances and fanning out writes to each of the instances to keep them up-to-date. The features I want to power with this are all gonna be low-consistency requirement features like social proof etc. and not correctness-critical things but even then I want to see how correct I can make it without too much effort.
I’m actually rethinking the in-memory component of this and am tempted to use a LRU cache for bitmaps that are actively being used and then have everything else be loaded from sharded SQLite files on-demand and then synced to SQLite on write with lower synchronous pragmas to keep the disk thrash down.
I want to power social proof for things like likes on posts as well which should be doable via one Roaring Bitmap per post that tracks UIDs of likers, then I can intersect those with the follow bitmaps too.
Ideally the entire graph should be able to scale to the size of your local disk and the RAM available will be used for caching hot values (though potentially a LOT of hot values since the bitmaps are so small).
When it comes to algorithm complexity, how relevant is the worst case? I understand that it gives a lower bound on performance, but without a sense of how probable the worst case is, does it really matter in practice?
levant is the worst case? I understand that it gives a lower bound on performance, but without a sense of how p
It is very relevant if the worst case is what happens on your specific workload!
For instance, here the worst is when the data is sorted (or almost sorted).
At Quickwit, we are building a log search engine. Most of the time, users run a query and want their logs sorted by timestamp. The data in the index is ordered in the order of ingestion, so our theoretical worst case is actually the most common case on our workload.
You will find a similar situation with sort algorithms. The first implementation of quicksort in libc was the naive one, where the pivot picked is the first element. It resulted in a complexity of O(n^2) if the data is almost sorted.
In the real world, a lot of the stuff we sort is already almost sorted, so it was incredibly slow.
They fixed it with a better pivot choice.
Also a lot of language standard library now prefer TimSort, which is specially optimized for this very common edge case.
It might matter if you’re writing something that’ll be exposed to the internet, because then, if there’s a way for the user to trigger the worst case, it could be used to DoS you.
IIRC a few years ago there was an issue with predictable hashtable keys in some language(s?), which could be used to DoS webservers.
You have rediscovered the concept of amortized complexity theory. The idea is to compute the complexity of a sequence of operations that might occur “in practice” and derive new worst-case bounds which are (theoretically) more favorable. Both amortized and worst-case analyses are relevant.
For example, tangent to @dzwdz’s comment, an (open-addressed) hash table usually has some sort of quadratic-time worst-case behavior, typically by forcing many rekeying operations under the hood. However, under typical use-cases, one of the n factors can become log n by amortization, which is tolerable. This matters because sometimes quadratic-time behaviors are found in the wild! Examples of this happened in Rust and in Python, Perl, PHP, and more. Users will write code “in practice” which exhibits worst-case behaviors.
It’s hard to tell based on the vague descriptions in the article, Scuba doesn’t sound exceptional to me. Visualising event volumes over time with the ability to filter by named attributes is structured logging 101, any passable log store (Elasticsearch/Datadog) will do it. The enforcing of sample rates in the schema sounds… interesting? Maybe a useful practice? But I struggled to follow that paragraph.
Disclaimer: I’ve read a lot about it but never used Scuba myself.
A few main differences between Scuba/Honeycomb and Elasticsearch:
Elasticsearch performs best with a strict schema defined, which is at odds with the free form key=value that Scuba encourages. In Scuba, you basically have 3 mandatory fields: event_id, timestamp, sampling_rate
Cosmetic difference but Kibana’s UI is not designed for this kind of drill-down/discovery exercise. It’s possible to do, but a bit clunky. Scuba is designed for this. I guess one could design a dedicated UI to run on top of ES.
Performance: because ES is document based, it’s difficult to get it to answer large aggregation queries in < 1s over multiple weeks worth of data.
The differences can look small, but they’re real and is what many ex-meta employees lament about when they say there’s no scuba equivalent in OSS.
There was a lot in this that resonated with my experience at Microsoft, but there’s one bit near the end that needs amplifying:
Location based salaries are discriminatory
If you have set up a company somewhere expensive (Silicon Valley, Seattle, London, wherever) and you require people to relocate there then you need to pay them for the cost of living premium. If you are allowing people to work anywhere in the world then there is no excuse for penalising the people who choose to live somewhere cheap (though requiring some minimal level of available Internet connectivity for remote work locations is fine).
Anything where you’re paying for input not output is a bad sign. The one that really annoyed me was that MS allowed you to buy a week of extra vacation time. People who take a healthy amount of vacation and don’t work long hours are vastly more productive than people who work for a long time without a break. The company is offering you the ability to pay them so that you can work more productively. This is totally the wrong incentive. This is why my contract at SCI, which is the template for people joining my team, has a requirement on the minimum amounts of leave people take, not the maximum. Smart people are far more likely to have their productivity suffer because they don’t stop working than because they take too much time off (for a lot of people I’ve worked with, the two weeks after they get back from a week off are far more productive than the four weeks before). Taking time off isn’t something that the company allows you to do as a reward, it’s something that the company requires that you do to ensure that you’re working at peak productivity. A tired person can introduce a bug in five minutes that it takes a week of debugging to fix. Managers need to be able to tell people that they’re working too hard and that they should stop for a few days.
If you are allowing people to work anywhere in the world then there is no excuse for penalising the people who choose to live somewhere cheap
I strongly agree with this. People’s cost of living can vary widely, and location is just one component. But we would never think it was OK to vary salary based on other lifestyle differences that affect those costs: whether someone is married or single, whether they have kids, whether they drive or use public transportation, whether they live in a house or an apartment.
I agree with your point, but I’d be willing to bet that there are a ton of people with kids who would totally support this. I mean, unpopular opinion, but parental leave is basically a bonus you get for making babies (not that I think it shouldn’t exist, but if there’s 8 weeks of parental leave, then everyone should be able to take 8 weeks off every few years or whatever).
To add to this, for mothers who give birth, it is typically at least 6 weeks of medical leave. Giving birth to a child is a physically traumatic event. Being a male I have obviously never experienced it first hand but I have witnessed my partner go through it twice. The suggestion that maternity leave is anything even close to resembling “time off” is, frankly, borderline offensive (and the fact that in the US paid maternity leave is not even legally required is practically criminal).
Heh yeah unlike vacation time where people come back more productive, I can only imagine that parental leave is seen as “they’re going to be scattered and sleep-deprived anyway… and that’s not going to be good for anyone”
A long time ago, I worked in a web dev startup where both founders were absolutely adamant that employees take as much parental leave as they were entitled to. Both had worked in places where the opposite was true, both had ended up missing out on the earliest bits of their kids’ lives (and one of them would sometimes say that it had also put an unreasonable burden on his wife), and both had regretted it deeply. Neither wanted to foist that upon anyone else, ever. It wasn’t directly relevant to me since I neither have nor want kids, but it’s the kind of little thing that reminds you that there are workplaces not run by complete sociopaths.
The same place also deliberately tried to avoid the kind of “workday as LAN party” grind that burns out everyone but the youngest devs, and pushes out older people who have family lives.
It then got acquired by a larger company, and the previously very healthy company culture promptly went to shit.
That’s true, but it’s usually still by choice. If I decide to take 8 weeks of to help the local firefighters, should it get the same preferential treatment? Not an easy question.
I think at some point it becomes a matter of choice vs. right. Do you have the right to spend 8 weeks helping the local firefighters? Usually no, in most cultures, but honestly I think it’d be kinda nice. Do you have the right to have children if you want to? Generally it’s considered pretty important that the answer is “yes” in most places, so making it so that your workplace has to give you leave for it is pretty reasonable. As gpanders points out, a significant amount of it is medical leave.
I think most people understand that some people have additional needs and that it’s okay, even virtuous, for society to compensate them for that.
Famously this idea is described as: “From each according to their ability, to each according to their need”
Now, I think everyone should have more holiday, but while we live in a system with limited paid time off then it makes sense to me that people with needy dependents should get proportionally more.
But people who live in a super expensive city literally need more. Having children is a lifestyle choice, just like living in an expensive city. I would take a different view if we, as a species, were critically under-populated. But since we’re not, I see no significant difference.
Edit: for something, like a disability, that is a condition, not a choice, I absolutely support accommodations, including additional pay and/or time off (whatever is helpful).
Raising children is labour and should be compensated. Extra paid time off for parents is part of that.
Also, for many people children are not really a choice. They’re accidental or it is unthinkable not to have them due to social conditioning, biological imperative, etc.
I’m not accusing you of this, but given our capitalist context, arguments that having children is a choice like any other tend to end up implying that poor people shouldn’t have kids, which is fucked up.
We could solve this as a society without prioritising parents especially by backing big transfers of income and political power to regular people.
Lots of people do realise they’re getting ripped off and that life could be better for everyone, but it won’t happen without more people getting organised. If you’re reading this and you’re not already in a feisty union or in some way building and exercising collective power then this is a good time to start, even if all you want is to protect your own quality of life.
Good international orgs: Industrial Workers of the World (IWW)
Good orgs in the UK: Unite, Acorn, Peoples Momentum
Good orgs in the USA: IWW, Democratic Socialists of America, your local renter or community union
I originally wrote a long sibling comment, but I’ll pare it down to a reply agreeing with and adding to your excellent comment!
Suppose that half of people in the US who have children had a choice, and that we want to accept their additional need as a reason for them to receive proportionally more. There are reasons over whose causes people have even less choice for which they might want to live in a city.
One reason might be that someone’s family has lived in a city for generations; do they have more an obligation to uproot their parents and grandparents to a lower cost-of-living area than someone has an obligation not to have a child?
Another reason might be that someone belongs to a minority group, in consequence of which, in defiance of reason but in light of their personal experience, they feel more welcome in cities than in lower cost-of-living areas. I can personally testify to this.
I think that having salaries that are not based on location is very reasonable. I think location-based salaries are also a decent compromise. I personally do not think that adjusting salaries based on the number of kids someone has or their identity etc. makes sense. I could see myself being swayed by new arguments.
Smart people are far more likely to have their productivity suffer because they don’t stop working than because they take too much time off (for a lot of people I’ve worked with, the two weeks after they get back from a week off are far more productive than the four weeks before). Taking time off isn’t something that the company allows you to do as a reward, it’s something that the company requires that you do to ensure that you’re working at peak productivity.
…and I think this is a very fundamental difference in different types of work. There’s design/project work, where you’re hired for your brain: deadlines are relatively distant, you build and design things, you make decisions and solve problems, and the quality and success of the result is very decoupled from the amount of hours put into it. And then there’s operations work, where you are hired for your output: not just flipping burgers or occupying a slot in a factory floor, but things like tech support, on-call or 24hr NOC/sysadmin work, working in construction or operating machinery, etc. In those your productivity is VERY coupled to the hours put into it: if you are present then the business is operating whether you don’t have anything or whether you’re working your ass off, and if you are not present then the business is not operating whether there is 1 Task Needing Doing or 1000. In those jobs a LOT of effort goes into scheduling people, and reliability and flexibility are valued much, much more highly by the business.
This is a distinction that most people don’t seem to know exists, for whatever reason. Nobody seems to talk about it but it very fundamentally shapes how the expectations of industry are shaped, which then shapes the laws and regulations and contracts and stuff that go into it. Maybe most people tend to work one or the other type of job their whole life?
I think there are two kinds of operations work, from your characterisation. If you are the receptionist or security guard, for example, there’s a huge difference between not doing your job at all and doing it badly. The gap between doing it badly and doing it well can also be large but, in normal circumstances, it is less than the complete shutdown that happens if you do nothing. In contrast, SRE-type work often involves doing the kind of things that, if you don’t do them until tomorrow then it’s not a disaster but if you do them wrong can result in data loss, reputation loss, and even penalty clauses for a customer.
Even this isn’t so clear cut. A receptionist who is overstressed and snaps at the wrong person may lose a million dollar contract.
The thing that surprised me recently was reading productivity studies on construction workers, which showed exactly the same shape curves as for knowledge workers. It turns out that making mistakes from from tiredness that offset benefits from working more is pretty universal.
Oh, it’s certainly not a clear cut division. But in my experience the differences between the two are much bigger than the differences within the two. They result in very different mindsets for me.
… which showed exactly the same shape curves as for knowledge workers.
Oooooh, interesting! Not surprising, but not entirely obvious. Do you remember where I can find those?
I’ve heard something similar from the local railway company, where (in the IT development department) you get more free time for a cut in pay..
A friend of mine sounded very happy about it at the time, you make a compelling argument against it. At the very least, the burnout that might result should be something that costs the company a lot more than what it saves by cutting pay.
Everyone one wants US salaries but nobody’s willing to give up all the local benefits (5 weeks paid holidays, 35h per week, strong worker rights, nationalized retirement, free healthcare…)
On the other hand, if someone is happy to work for $50k because they’re 12 timezone hours away and live on a beach in a $10k bungalow and they have full time domestic help for a few thousand bucks a year, I don’t know why a company should hire them at $500k just because a few other employees live in Palo Alto.
Companies will only pay what they have to pay. That’s how free markets work.
It gets more complicated when it’s an employee’s choice to move from Manhattan to Vietnam, of course. Should the company reduce their pay just because the cost of living for the remote employee has dropped? That seems creepy.
There aren’t that many highly paid jobs in which you can just choose to live anywhere. Programmers are pretty lucky.
Companies will only pay what they have to pay. That’s how free markets work.
Markets will eventually price goods based on both supply and demand. Prices go up if demand outstrips supply and up if supply outstrips demand. The company offering $50K is usually not the only source of demand.
If you make this person a $50K offer, then another company can offer them $100K and they’ll be really motivated to take it and that company is still saving money relative to hiring someone for $500K. Retention is usually cheaper than hiring, so offering a salary that encourages people to leave is a bad long-term strategy. We’ve seen this with Indian outsourcing companies: their competent workers rapidly get hired away at higher salaries and so they’re left with inexperienced or incompetent people (or they have to match international salary rates).
If you’re hiring remote workers anywhere in the world, then you’re competing for talent not just with companies local to that person but with any other company that offers remote work. If you have someone who you’re paying $50K, they don’t have to offer $500K to steal them, $100K is fine. But then someone else comes along and hires them for $200K, and then someone else offers $300K. And they’re still cheaper than those $500K folks, so the ones in the bay area are getting fewer offers. If you’re getting $500K of value from someone doing the job, then it makes sense to offer $400K to hire someone talented away from the competition. If you’re getting $1M of value from someone, then offering $500K anywhere in the world gives you a much better pool of applicants than offering $500K to people in the Manhattan and $50K to people in Vietnam.
Salaries probably won’t eventually converge on the current bay area rates, but there are a lot more smart people that don’t live in the bay area than do and an increasing number of companies competing for their labour. When I was looking for a job last year, I got several pings from companies that do full remote and don’t do location-based salaries. If you do, you’re probably not competitive as an employer.
I’ve hired a few thousand people across 6 continents, and (unfortunately) had to lay off some as well. Everything that you wrote in this response is correct in my experience. I’m not arguing for or against, or defending, or promoting. Like what you wrote here, I was simply pointing out reality. Reality doesn’t ever seem to care what I think about it.
I have always paid well when I hired people. But when hiring someone in Prague or New Delhi or St Petersberg, I wasn’t typically offering Bay Area rates. I may have paid better than what most people in Czech Republic or India or Russia make, but as you suggest, I was offering based on the market prices and how badly I wanted / needed a particular employee, and how badly other companies wanted them. I’m super proud to have had a lot of employees targeted by other companies for recruitment, and super happy for their successes after leaving my teams. I always tried to make it hard for other companies to poach, but good people are always worth paying dearly to have, and it’s also good for people to get to try out some new challenges from time to time.
What’s the largest application written in Gleam? I’d love to take a look a non toy code base to get a feel for how it all works together: language + ecosystem + deployment.
Well that’s a first for me, getting called an LLM. :) I can assure you I wrote it all, but point taken. Here I was worried that the prose was too short! Guess not. The code mistake I can fix, I’ll make it more clear that it’s not a full and complete function. I thought that was obvious – which it is as y’all have noticed, but it’s clearly sending the wrong vibe
I can understand the comment. Maybe I should remove the ‘main’ function so that it doesn’t look like a full and complete function or somehow indicate that elsewise… I can perhaps add a comment at the top, or a function call to some function that returns the context… I tried to leave out the extra code like that but probably should update it to make that more clear, thanks
I hope you’ll find the time and energy to keep on writing these. It’s a pleasure to read.
Nice work on Sophia, it’s a neat small language.
Not sure if you’re open to feature requests, but exposing host functions, similar to wasm/lua/etc. would be great. One could write most of the logic in Sophia but hook into the extensive go ecosystem when needed.
Im always open for feature request, you can simply create an issue for one :). I would know how to implement that but i will do some research about it.
Normally I don’t submit these small blog like posts here but pagefind was so easy to setup and integrate in my own static site generator and looks beautiful. It even defects images, the default UI looks nice and it responds fast. I think there are more people here that run a static site and would be interested, including an example of how it looks with 10+ years of static site content. I just recently heard about pagefind, it was discussed here almost a year ago ( https://lobste.rs/s/pvkg80/static_low_bandwidth_search_at_scale ) but I missed that…
That used to be Heroku’s bread and butter. It wasn’t cheap but worked so well that productivity gains far outweighed the costs.
AWS AppRunner is an okay alternative to Google Cloud Run, but nowhere near as flexible nor fast to provision.
Heroku’s fall is crazy. It had the best workflow imaginable.
The pricing was already getting untenable for many workloads when Salesforce took over (AFAICT they basically never dropped their price / compute as everyone else did), but the shift from “best dev experience available” to “handy way to build salesforce apps” really ran it into the ground.
https://github.com/shopify/job-iteration Shopify has a gem precisely for that. And even have another gem thar gives a UI to run one-off tasks that one often needs to run that takes advantage of the ‘resumability’
I believe this is the gem you were referring to:
https://github.com/Shopify/maintenance_tasks
Sidekiq now also has iterable jobs built in: https://github.com/sidekiq/sidekiq/wiki/Iteration
Slight OT: is there guidance on how to deploy an application that uses sqlite. Do you need to rely on a smart load balancer to guarantee no-downtime deployments?
You can use systemd socket activation or s similar socket handoff technique. The idea is another process opens the sockets and hands them off to your service. When the service reloads it takes them back, then hands them to the next instance. There is a slight delay but no dropped connections.
One thing I realized while thinking about this is it’s only the write requests you need to worry about. If you have a load balancer you can route all the GET requests to a separate process. That process can safely do rolling restarts or staged rollouts.
Honestly if you want to guarantee no-downtime deployments, you will suffer a lot less just using postgres or whatever SQL flavor your provider likely offers, even in unmanaged form. You can get the experience of “my database is a single file to backup” by calling
sqldump > backup.sql
and taking that.Sqlite is nice if you want low ops and don’t have that many durability requirements but if you’re at downtime prevention you’re going to have to do a bunch of work anyways.
I understand the rationale, but requiring TLS for services running locally/laptop/same VM can be a PITA at times. h2c was great for this.
Unsolicited feedback;
thank you! updated FAQ https://andrewarrow.dev/frame/faq
Hi everyone. I’m launching Linguist Translate, an open-source, full-featured translation solution with an embedded offline translator based on the Bergamot Project created by Mozilla.
Site: https://linguister.io
GitHub: https://github.com/translate-tools/linguist
Today, Linguist is launched on ProductHunt. Support the project who really care about privacy: https://www.producthunt.com/posts/linguist-translate
Linguist is not just a wrapper over Google Translator like many other extensions. You can use any translation service with Linguist, thanks to custom translators! You may even deploy any machine translation (like LibreTranslate) on your localhost and then add this service to Linguist.
All features are included: text translation, full-page translation, selected text translation, Text-To-Speech, dictionary, history, and even more.
I just installed it and the default was Google Translate? You headline here is “privacy focussed offline translation”, which seemed to imply that it was using Bergamot by default. ☹️
Just pick Bergamot in preferences. Privacy is not about “trust by default”.
A Bergamot Translator is not the only feature about privacy in Linguist. Linguist protect your privacy by design and give you abilities that is unreachable in another extensions. Check a custom translators in Linguist, for example. Do you know any other extension who can do like that?
I mean, can you deploy machine translation on you local machine and then use this service with another browser extension for translation? As i know, there is no such features in other addons. When i implemented this feature, people told be “it’s just madness”. But with Linguist you can do it.
With Linguist - any translation service or LLM in universe will be served for you as translators. Linguist features will be grow, but all features will be based on translator you prefer in options.
Currently we work on images translations. Do you know how it implemented in most solutions? Addons just sent images from a pages to remote server, then use google translation and return it to you. Linguist will recognize text locally, then use translator you prefer, to translate text, and then re-draw image locally. So you still may sent text from image to internet, but even if you will try to translate your WhatsApp messages with sensitive content - nobody will see your cat, but only text from image may be compromised. If you will use offline translator, then even your text will be private.
This is too long story to explain it on every press release, but this is why Linguist is private, its really care about your privacy unlike other translators who just make money on your data.
Frankly, I disagree. If your submission here is titled “privacy focused offline”, it shouldn’t default to an online service.
I like the feature set and it’s a true value proposition on top of what e.g., Firefox has as a built-in default, privacy-focused offline translation. Custom dictionaries, learning, history and lookup. That’s all really cool. But Linguist shouldn’t call itself what it’s not.
Ok fellow, how we should rename a Linguist?
well, probably not “offline translator”
Linguist is not an “offline translator”, it is full-featured translation solution. The title of current post is “offline translation in your browser” that is true, if you can do 3 clicks.
Then don’t advertise it as “privacy-focussed, offline translation”.
Why?
Because it’s deceptive bullshit.
Why?
The headline features you advertised are things that the program can technically do, but which it won’t do by default because it makes it work worse.
“Offline” means offline. If the first thing your translator does is reach out to the internet to talk to Google Translate, it’s not offline. Call it “offline-capable” if you want, that’s about the right level of weasel-words. The explanation of it on the website is perfectly reasonable.
Why not make Bergamot the default? People can always choose to use Google, but as @freddyb pointed out, using Google by default clashes a bit with the valuable privacy angle.
Good question, actually. This is trade off between privacy and available languages. Bergamot translator currently have not as much languages as Google Translator have.
Most users don’t care about offline translation, so translation extension that can’t translate Japanese to Hebrew not so good. So default is Google translator which have a lot of languages. But users who need offline translation, may change translator to Bergamot in 3 clicks.
When Bergamot will have enough languages directions, we will change default to Bergamot.
Thanks for creating it! I’ve been using it for the past year and it’s been an amazing improvement over the awful translator Brave has built in.
Thank you for use a Linguist.
I understand you here, i felt the same. Actually, I have created a Linguist when i changed Chrome to Firefox, because Firefox did not have embedded translator at all, and another extensions looks not enough good.
HTTP is the new FastCGI. Do shared hosts not support running persistent processes? If not, they should. File-based script hosting is dead, it’s not general enough and every app wants full control of when things happen.
Given how many users are typically given accounts on a single host, this would be extremely wasteful for memory compared to CGI. Shared hosting (at least back in the day) was dramatically cheaper specifically because you could fit a lot more customers on a single machine.
It’s not general enough to replace a VPS, but it’s general enough for me. Just because it doesn’t solve every problem out there doesn’t mean it’s dead.
Thanks to systemd socket activation is actually trivial to have a http server that starts on demand and shuts down after some period of inactivity, effectively not wasting any ram “just to sit there”. Sorry for the twitter link, but if someone wants to follow I posted description recently: https://x.com/dpc_pw/status/1797540651229438147
The ability to do this was recently added to SvelteKit: https://kit.svelte.dev/docs/adapter-node#socket-activation
This opens the way for hosting many SvelteKit apps on a single host with automatic shutdown and near instant startup.
The author of the above feature has a project that packages up SvelteKit apps, a Node.js executable and glibc++ (The total size of the final image is approximately 37 MB). This is deployed as a systemd ‘Portable Service’: https://github.com/karimfromjordan/sveltekit-systemd
Two things:
Well, but keep in mind that there’s two “modes” of FastCGI:
Option 1 is the most well-known, because it makes sense for php-fpm. But IMO option 2 is the more interesting one, because it gets you the performance benefits of a persistent process while preserving a shared host’s ability to keep prices low by over-subscribing a server where many of the hosted sites and apps have very low-traffic.
Personally, I want option 2 but for HTTP — call it a “reverse-proxy scheduler” model. Use config to mount a small http-speaking app at a particular path in your site’s URL hierarchy, and let the main web server spawn/kill it as it sees fit. This doesn’t exist, but IMO it should; and glancing around, it doesn’t seem like I’m the only one who wants this.
Isn’t that how Heroku worked? They would run your HTTP-speaking application in a container, which AFAIK would be killed off if idle for a while. Also I think a lot of these modern cloud offerings like AWS and Azure have something that works in a similar way. I think the main problem with these things is that it’s a bit heavier-weight and more expensive. Sounds very much like your “reverse-proxy scheduler”
Yeah, as I said before here I have thought FastCGI should have been HTTP for over 25 years.
Have you looked at Wagi? https://github.com/deislabs/wagi
wasm aims to be the spiritual successor to some of the cgi functionalities mentioned in this thread.
Yeah so I was going to “invent” AGI (Andy’s Gateway Interface) – which is just any HTTP server with FastCGI-style process management …
The key points are
This does not exist currently, but there’s no reason it shouldn’t!
Most shared hosts don’t support persistent processes. Dreamhost doesn’t.
Mythic Beasts does, but somewhat surprisingly it exposes
systemd
config files in your home dir:https://www.mythic-beasts.com/support/hosting/python (I haven’t set this up yet, but I will)
OpalStack has some pretty weird advice for Go servers: https://community.opalstack.com/d/695-golang-in-opalstack
Like basically using cron jobs to restart them … this seems like “non-support”.
My guess that it creates more of a server management headache. But it is something that the cloud providers have solved, and it gives a better user experience.
Systemd actually provides most of a solution here. Use socket activation to start your daemon when a connection comes in and then your daemon can exit itself after some idle time. If another tcp connection comes in, Systemd will reuse the connection to the daemon while it still exists and start the daemon again if it has exited.
Yeah I’m definitely going to kick the tires on this … Last week, I set up uswgi on OpalStack, and I also wanted to try the recommended Python thing on Mythic Beasts
Don’t forget about scgi too.
It boggles my mind they didn’t start with this plugin/add-on implementation but I’m glad to see they listened to their users.
Everything always looks obvious in hindsight though.
The degree of outrage was surprising; I sympathize with the maintainer here.
I’m surprised that you find it surprising, genuinely. Every hype cycle creates a corresponding backlash.
I’m not sure I would have taken too much notice of it. There is a ton of negative sentiment from tech people, particularly on the orange site. Best to ignore most of it and focus on people who have something constructive to offer.
I usually agree with hindsight being 20/20 but adding AI to a terminal application automatically on a minor software upgrade was not going be received well. Same would have applied to crypto…
It didn’t add it automatically. You had to complete several steps including providing your own API key for it to start working on your explicit request.
I am the first to object to random AI integration, have no use for this feature, and also have other reasons why I don’t and probably will never again use iTerm2. All of that said:
Although the feature was there in the core, it didn’t work out of the box. You needed to explicitly configure it to use an API key that you provided. I am quite surprised that it would be problematic for people.
Weird take. Obviously you could do everything in untyped lambda calculus, but that sucks. Likewise generics significantly reduce the need to use the any type, eliminating most remaining untype checked code. Adoption is low because it’s used with restraint.
There’s absolutely zero doubt that generics are useful. Literally everyone agrees with that!
What is also true is that generics have costs, across many dimensions. I think it’s also true that literally everyone agrees that generics are non-free.
What is curious though, is that it seems that there’s a sizable population who don’t acknowledge that cost. Which looks like:
Person A: generics are useful for X but they have costs C.
Person B: no, generics are useful for Y.
Another observation here is that the overall type system complexity is a spectrum. And this tradeoff-vs-zero cost dialog tends to repeat at any point on this spectrum during the language evolution.
EDIT: the canonical example of this effect couple of notches up the expressiveness scale: https://github.com/fsharp/fslang-suggestions/issues/243#issuecomment-916079347
I have very mixed feelings about this.
Taking Rust as an example, I think the complexity of the trait system (type classes with type projections, including type function projections) is necessary complexity to make Rust’s attempt at safe, “zero-cost” concurrency viable. On the other hand, I can’t deny that some of the way I see the trait system used in the ecosystem (and even, unfortunately, in certain recent, dubious, as-yet-unstable additions the standard library) is awful library design and obviously the sophomoric obsession with “expressive type level programming” that Don Syme alludes to. I try to avoid depending on crates like this as much as I can, and my own code rarely ever involves defining new traits.
But how can we tame this jungle of expressiveness without losing the ability to prevent classes of bugs via automated, formal program analysis? One option perhaps is to have a clear distinction between the “application language” and the “systems language,” allowing you to omit features in each and create a simpler language in each category, whose features together combine via sum (application language + systems language) instead of product (application features * systems features).
Baring that, I would say that there is always the informal technique of culture development so as to avoid the use of terrible type programming. In other words, I want a language which can expression typenum, with a community of practitioners who are wise enough never to actually use typenum. I don’t see why that should be beyond us.
My gut feeling is that there’s still room for improvement language design wise, that we are still pretty far from expressiveness-complexity Pareto frontier.
That’s very unsubstantiated feeling, but two specific observations make me feel that way:
One possible mitigation is culture and idiom. Maintainers have a lot of soft power here. They write the reference documentation that people read when onboarding to the language. Some slogan about “concrete is better than abstract” could do a lot of work (it would probably be invoked legalistically in many cases, but that seems like the lesser evil to me). I think culture does a lot of work to keep Go code simple even since generics have been released.
I can somewhat easily envision someone writing Rust closer to how they would write C. I could even see the Linux project implementing guidelines for Rust that aim to keep it simple, and maybe other projects adopt those guidelines. Maybe they get enough traction to influence Rust culture more broadly?
We could probably do a better job with the language we use to describe some features. For example, calling a feature advanced implies that it’s users are not beginners. If you instead call a feature niche, you imply that (1) it is not meant to be commonly used and (2) people do not need to understand it to be productive.
I’m confused about the distinction between “everyone agrees that generics are non-free” and “there’s a sizable population who don’t acknowledge that cost”. If there are people who don’t acknowledge the cost, doesn’t that imply disagreement that generics are non-free?
Aside from that confusion, I agree. In many of the debates I’ve witnessed about Go and generics over the years, the rabidly anti-Go people (who have always been extremely numerous, i.e., I’m not exactly nut-picking) have typically refused to concede any downside to generics whatsoever. Similarly, they’ve refused to acknowledge any utility in standardization (“most Go code is written in a very similar way”). And in fairness, I didn’t really think much of the standardization property before I used Go in earnest either, but then again I didn’t go around arguing decisively against it as though I had some kind of expertise with or perfect knowledge about it.
By contrast, within the Go community there has usually been a pretty robust debate about costs and benefits. Marginally few people would argue that generics confer no benefit whatsoever, for example.
I might be using wrong words here, so let me make this more specific. Let’s take a simple statement “generics make compiler harder to implement”. I can see people expressing the following positions:
From my observation of such discussions, I think position 4 is puzzlingly prevalent. Well, maybe I am misunderstanding thing and that’s actually position 1, but I find that unlikely. But also that’s squishi human theory-of-mind stuff, so I might be very wrong here.
I think part of the disconnect here is that apparently you’re very concerned about complexity in the compiler. I didn’t realise that was your concern until this comment.
Obviously a slow, buggy compiler is bad, but I generally view the role of the compiler as eating complexity off the plate of the programmer; instead the costs I’m thinking about are those that fall on the users of compilers.
No, I am not very concerned about implementation complexity. I use this as the first example because:
As I’ve written in my other comment(and, before that, at https://matklad.github.io/2021/02/24/another-generic-dilemma.html), my central concern is the ecosystem cost in the form of more-complex-than-it-needs-to-be patterns and libraries. And, to repeat, Go is an interesting case here because it is uniquely positioned to push back exactly against this kind of pressure.
I guess I don’t see the costs. You can still use untyped or first-order-typed code. Explain the costs like I’m five?
Notably go generics don’t allow for compile time computation unlike ml-derivatives.
One cost to the “generic” generics I am very well aware of. I was at JetBrains when support for Rust and Go was added to the platform, what was later to become GoLand and RustRover.
For Go, the story was that the language semantics was just done at one point, and devs continued to hack on actual IDE features. For Rust, well, we are still trying to cover the entire language! I don’t think either rust-analyzer or RustRover support all type system features at the moment?
Now, current Go generics are much simpler than what Rust had circa 2015, so the situation is by far not that bad. But this is still a big increase in implementation complexity relative to what’s been there before.
What I think is the biggest cost is the effect on the ecosystem. You can’t actually stick to first-order code, because the libraries you use won’t! There’s definitely a tendency to push ecosystem towards complexity, which plays out in Rust for example. See, e.g., boats comment. But here I can confidently say you don’t need to go all the way up to Rust to get you into trouble — I’ve definitely lost some time to unnecessarily generic Java 7. I am very curious how this plays out in Go though! Go has very strong focus on simplicity, so perhaps they’ll reign this social tendency in?
Another cost of generics is compile time or runtime. Generics are either slow to compile, or slow to run. This is also often a cost imposed on you by your dependencies.
Finally, there’s this whole expressively treadmill: languages start with simple generics, but then the supports tends to grow until past the breaking point. Two prominent examples here are Java type system becoming unsound without anyone noticing, and Swift type system growing until it could encode undecidable word-equivalence in a semi-group problem.
Per your other comment, I’m in camp 3: it’s a one time cost, and unless it takes literal years to pay it, we can mostly discount it: the compiler team is tiny compared to the size of the whole community, so the only relevant cost I see here is opportunity cost: what else could the compiler team do instead of implementing generics, that would make life even better for everyone else?
I believe Ocaml disproved that a number of decades ago.
Its solution required a small compromise (to avoid heap allocating integers, their range is cut in half), but the result is dead simple: generic code can copy integers/pointers around, and that’s about it. Compile once, run on any type. And when the GC comes in, discriminating between integers and pointers is easy: integers are odd, pointers are even. The result is fast to compile and run, as well as fairly simple to implement. And if we needs natively sized integers, we can still heap allocate them (the standard library has explicit support for such).
Oh but you’d have to think of that up front, and it’s pretty clear to me the original Go devs didn’t. I mean I have implemented a statically typed scripting language myself, and I can tell from experience: generics aren’t that hard to implement. The Go team would have known that if they tried.
This slippery slope argument also applies to languages that don’t have generics to begin with. See Go itself for instance. The problem doesn’t come from generics, it comes from an unclear or expanding scope. If you want to avoid such bloat, you need to address a specific niche, make sure you address it well, and then promote your language for that niche only.
I’m pretty sure Go acquired generics for one of two reasons: either it wasn’t addressing the niche it was supposed to address well enough, and the compiler team had to say “oops” and add them; or people started using it outside of its intended niche, and the compiler team felt compelled to support those new use cases.
Not really, Ocaml uses standard solution in the “slow code” corner of design space: everything is a pointer (with an important carve out for ints, floats, and other primitive types).
This is the case where generics make even non-generic code slower.
That’s fair, but I still have a trick up my sleeve: there’s an intermediate solution between full monomorphisation and dispatch/everything-is-a-pointer. In C++ for instance, most template instantiations differ by one thing and one thing alone:
sizeof()
.Which narrows the choice quite a bit. Now we’re choosing between compiling once per type size, and passing an additional parameter to the generic functions (the type size). Sure you won’t get the fancy stuff like copy constructors, but the compilation & runtime costs involved here are much, much tamer than the classic solutions.
Sounds like golang’s gcshape monomorphization mentioned by ~mxey?
It actually does, thanks for the link. And from the look of it, it does sound like it’s more complicated than just
sizeof()
, which I would expect if you want to support stuff like non-shallow generic copies. I’ll look it up.I’m very suspicious of slippery slope arguments.
I don’t think that’s true. That’s true of much more complex higher order type systems than go’s generics.
Eh unsound Java still rejects more incorrect programs and object-riddled Java.
I think it is true of any possible implementation of generics. The two implementation choices are dynamic dispatch or monomorphisation. Dynamic dispatch includes a run-time cost, which may be small but also adds a bit more in terms of impeding inlining. Monomorphisation incurs a compile-time cost because you have to create multiple copies of the function.
Slower at compile time, but not necessarily slow to compile.
Can you expand on that? I’m guessing that you mean that a program expressed without generics but solving the same problem will require more parsing and may end up being slower to compile?
Sorry, I just meant that even though monomorphisation takes non-zero time it doesn’t follow that overall compilation will be even perceptibly slower. Not free but maybe still cheap.
That and maybe some optimizations (during compile) can be done when the compiler is smart about generics rather than not knowing that similar things are actually the same.
Without generics, you also have to create multiple copies of the function.
The problem with C++ / Rust style generics is that monomorphized functions are generated at the use site, where the concrete types are known. They create lots of duplicate copies of monomorphized functions that must subsequently be deduplicated.
This is more of a problem for C++ than Rust, because C++ does this per compilation unit and so the linker typically throws away around 90% of the generated object code in the final link step. Rust doesn’t have the same separate compilation model, but still suffers from some of the same cases where the generated functions are identical and you need to merge them.
Or you do dynamic dispatch and have one copy of the function that can do the same work, just without any specialisation.
The tricky thing for a compiler (and I don’t know of any that are good at this) is working out when monomorphisation is a good idea. Both Rust and C# (and some ML dialects) have attributes that let you control whether something should be monomorphised. Go uses an approach where they monomorphise based on the size of types but do dynamic dispatch for method calls.
If I recall correctly OCaml does neither monomorphisation nor dynamic dispatch. With the possible exception of special cases like structural equality, generic code simply treat generic data as opaque pointers, and as such runs exactly as fast as a monomorphic version would have.
The GC does some dynamic dispatch to distinguish integers from pointers, and integer arithmetic does do some gymnastic to ignore the least significant bit (set to 1 to distinguish them from pointers), so the cost is not nil. As high as actual dynamic dispatch though? I have my doubts.
Go isn’t a particularly smart compiler, which makes benchmarking the overhead of the function call relatively straightforward:
https://gist.github.com/matklad/d7e36f031a38a9a7b3b8a378486a6a91
There’s just more indirection with generics, relative to interface passing. And the alternative to generics is not always an interface — often it is “this code doesn’t actually need to be generic”. And at that point, the difference might be stark: either you don’t monomorphise, in which case the non-generic code, by virtue of inlining, becomes massively faster, or you monomorphise, and now your thing compiles many times slower.
You’re talking about a language that added generics after the fact. This adds constraints they likely wouldn’t have had if they did it from the start, and is much more likely to bias heavily against generics. Try the same with OCaml, I bet the results would be very different.
Now benchmark against using the any type, which is what you’d use in the absence of generics. I suspect that’s not any faster than generics.
If you don’t have generics, most of the time, rather than using
any
, you refactor the code to not be generic. In any case, any is not slow:This makes sense — it’s still static dispatch after the downcast, and the downcast should be speculated right through.
You do have to be careful about inlining and devirualization. In this case, the interface version is being inlined and devirtualized and so it ends up doing the exact same code as the direct version. Adding
//go:noinline
annotations to the four functions changes the results from (on my PC on Go 1.22.4)to
which matches what I expected: generics are implemented using dictionary passing, so it’s a virtual dispatch call just like interfaces, and the any version is going to do slightly more work than the increment checking the type every loop.
People like to claim that Go isn’t a particularly smart compiler, but I find that it does do quite a bit of useful optimizations and instead chooses to skip out on those that are less bang for the buck. For example, in this case, it actually executes every loop and increments instead of just loading a constant like many C compilers would to (IMO) dubious benefit.
Thanks, I take my words back that it’s easy to benchmark Go code, I didn’t realize that the compiler already does devirtualization!
It only devirtualizes in extremely limited circumstances. Anything more complex than a micro benchmark and it won’t devirtualize 🙃
Your commitment to educating others with your unique experiences is incredible. Thank you for these replies.
<3 that’s an extremely lovely characterization of my procrastination!
Well I stand corrected on the performance - I’m actually quite surprised that go generics are being expanded to something that performs worse.
Typically the
any
type isn’t what we’d use in the absence of generics. I’ve written a lot of Go since 2012 and only a vanishingly small percentage of it usesany
for generic use cases. Far more frequently, I just writetype FooLinkedList struct { Foo Foo; Next *FooLinkedList }
because it’s both more performant and more ergonomic than anany
-based linked list with type assertions at the boundary.I was using linked lists a lot when was writing code in C. It is interesting that I never used linked lists in Go during the last 12 years. Standard slices work exceptionally well in places where C would require linked lists. As a bonus, slices induce less overhead on Go garbage collector, since they are free from
Next
andPrev
pointer chasing.IMHO, slices is the best concept in Go. They are fast, they are universal, they allow re-using memory and saving memory allocations via
a = a[:0]
trick.I agree. I don’t use linked lists very often, and my point wasn’t that linked lists are a particularly good container; only that in the rare instances when I need to do something generic, I’m only very rarely reaching for
any
. There’s almost always a simpler solution. Usually it’s the builtin generic types, but even when it’s not, there’s almost always a better solution thanany
.The cost is that your dumb coworkers can have more ways to write horrible code.
Go is the only language I know of that takes “your coworkers are dumb” as a design principle
In English, “you may have dumb coworkers” does not mean “all of your coworkers are dumb”. Moreover, Go doesn’t have any design principle with respect to “dumb coworkers”, but it does care about keeping the cognitive burden small (you don’t have to be “dumb” to make mistakes or to want to spend your cognitive budget on business rather than language problems). I don’t think that’s uniquely a concern that Go has expressed–every language which exists because C or C++ or JavaScript or etc are too error prone is implicitly expressing concern for cognitive burden. Everyone who argues that static type systems provide guard rails which make it more difficult for people to write certain kinds of bad code is making essentially the same argument.
You aren’t taking into account what’s the alternative to generics. People aren’t just going to stand idly and write the same function twelve times, they are going to bolt on macro systems (m4, cpp, fuck even jinja) on top of the source code, implement custom code preprocessors or parsers (pretty much every big C++ project of the 90s), use magic comments, funky build system extensions (you don’t know how far you can get with CMake and some regexes), etc. One way or another the written code is going to be generic… and I tend to think it’s much better if it’s in a language-sanctioned way rather than every project reinventing its own take on it. Today in C++ people do so much more things in-language than in weird external tools compared to 20 years ago, and that’s thanks to the continuous increase in expressive power of its type system.
Alternatively, people may think more and come up with simpler solution, which doesn’t require generics and external code generation. From my experience well-thought interface-based solutions in Go are easier to read and reason about than generic-based solutions.
Update: when talking about interfaces in Go, people frequently think about empty interfaces and forget about non-empty interfaces exposing the minimal set of functions needed for solving the given generic task.
My concern was (and sort of still is) that people would start writing Go libraries with gratuitous abstraction, like they do with other languages that embrace generics. “Just don’t use those libraries” isn’t much of a consolation if that’s the only available library for a given domain nor if the gratuitous abstraction becomes a property of a vast chunk of the ecosystem as with other languages. I’m happy to report that my concern has so far been misplaced–the Go community has done a fantastic job about exercising restraint.
Readability suffers: more symbols, one letter types, etc…
Is that true? IIRC, if so, wouldn’t they have been added a long time ago, with less resistance both from the PL creators and the community? I’ve (unfortunately) only recent had to delve back into Go, after previously using it professionally a few years prior.
Yes. This is true. Every one agrees that generics are beneficial. Luckily, go language designers, besides knowing quite a lot about benefits of generics, are also keenly aware of the costs. The costs are the reason why this isn’t a no-brainier feature to add in 0.1 version of the language.
As my old hardware prof told me, even the most crappy CPU feature have uses! “It is useful” can’t be a reason to add something, you need to compare it to the costs.
I’m glad to see the “used with restraint”. I was worried (and still am concerned) that people were going to write gratuitously abstract libraries like we see in other languages. But so far I’ve been happy with the degree to which people have used them appropriately in the ecosystem.
S3 charges for unauthorized requests?? That’s crazy!
The house always wins.
Using S3 sounds like a great way to set yourself up to be financially trolled hard. There are lots of unlikable companies people hold grudges against. Many many companies use S3 nowadays even on their corporate websites via CMSes for image hosting etc.
Can think of many companies I’d like to punish, but that would just further enrich Amazon.
I’m probably being pedantic, but pointers don’t really have “overhead”, at least not in the sense that most OOP languages have overhead for objects. A pointer in Go is exactly an address, no more no less. Dereferencing a pointer takes some time, but not necessarily more than the time it would take to pass a copy. The main issue with pointers is that you have to be careful about how you use them or else they will escape the stack resulting in an allocation which is expensive.
Unfortunately, Go doesn’t have very good tooling for flagging these allocations (that I know of, anyway). I would love for someone to build a VS Code extension that highlights allocations or a linter that lets me annotate a line or function with //go:noalloc and then yells loudly if that line/function starts to allocate.
I’d argue the tooling for Go is decent.
Here’s an example that shows how to get started with basic heap escape analysis https://landontclipp.github.io/blog/2023/07/15/analyzing-go-heap-escapes/
Huh, I’ve Googled for this many times and never come across that. Thanks for sharing!
I’d be curious to hear how this would work in a production setting. Having a single instance makes the current implementation consistent at the expense of being “HA”.
I believe it’s not as simple as keeping a “warm” second instance.
Looking forward to part3
Yeah, I’ve been thinking about having shared-nothing instances and fanning out writes to each of the instances to keep them up-to-date. The features I want to power with this are all gonna be low-consistency requirement features like social proof etc. and not correctness-critical things but even then I want to see how correct I can make it without too much effort.
I’m actually rethinking the in-memory component of this and am tempted to use a LRU cache for bitmaps that are actively being used and then have everything else be loaded from sharded SQLite files on-demand and then synced to SQLite on write with lower synchronous pragmas to keep the disk thrash down.
I want to power social proof for things like likes on posts as well which should be doable via one Roaring Bitmap per post that tracks UIDs of likers, then I can intersect those with the follow bitmaps too.
Ideally the entire graph should be able to scale to the size of your local disk and the RAM available will be used for caching hot values (though potentially a LOT of hot values since the bitmaps are so small).
When it comes to algorithm complexity, how relevant is the worst case? I understand that it gives a lower bound on performance, but without a sense of how probable the worst case is, does it really matter in practice?
It is very relevant if the worst case is what happens on your specific workload! For instance, here the worst is when the data is sorted (or almost sorted).
At Quickwit, we are building a log search engine. Most of the time, users run a query and want their logs sorted by timestamp. The data in the index is ordered in the order of ingestion, so our theoretical worst case is actually the most common case on our workload.
You will find a similar situation with sort algorithms. The first implementation of quicksort in libc was the naive one, where the pivot picked is the first element. It resulted in a complexity of O(n^2) if the data is almost sorted. In the real world, a lot of the stuff we sort is already almost sorted, so it was incredibly slow.
They fixed it with a better pivot choice. Also a lot of language standard library now prefer TimSort, which is specially optimized for this very common edge case.
I hadn’t considered the results being already sorted was a common case for chronological ordering. Makes perfect sense. Thanks for the explanation.
It might matter if you’re writing something that’ll be exposed to the internet, because then, if there’s a way for the user to trigger the worst case, it could be used to DoS you.
IIRC a few years ago there was an issue with predictable hashtable keys in some language(s?), which could be used to DoS webservers.
You have rediscovered the concept of amortized complexity theory. The idea is to compute the complexity of a sequence of operations that might occur “in practice” and derive new worst-case bounds which are (theoretically) more favorable. Both amortized and worst-case analyses are relevant.
For example, tangent to @dzwdz’s comment, an (open-addressed) hash table usually has some sort of quadratic-time worst-case behavior, typically by forcing many rekeying operations under the hood. However, under typical use-cases, one of the
n
factors can becomelog n
by amortization, which is tolerable. This matters because sometimes quadratic-time behaviors are found in the wild! Examples of this happened in Rust and in Python, Perl, PHP, and more. Users will write code “in practice” which exhibits worst-case behaviors.Besides DoS, it matters for work done in batches, if the new batch is dependent on the aggregate result of the previous batch.
It’s hard to tell based on the vague descriptions in the article, Scuba doesn’t sound exceptional to me. Visualising event volumes over time with the ability to filter by named attributes is structured logging 101, any passable log store (Elasticsearch/Datadog) will do it. The enforcing of sample rates in the schema sounds… interesting? Maybe a useful practice? But I struggled to follow that paragraph.
Disclaimer: I’ve read a lot about it but never used Scuba myself.
A few main differences between Scuba/Honeycomb and Elasticsearch:
Elasticsearch performs best with a strict schema defined, which is at odds with the free form key=value that Scuba encourages. In Scuba, you basically have 3 mandatory fields: event_id, timestamp, sampling_rate
Cosmetic difference but Kibana’s UI is not designed for this kind of drill-down/discovery exercise. It’s possible to do, but a bit clunky. Scuba is designed for this. I guess one could design a dedicated UI to run on top of ES.
Performance: because ES is document based, it’s difficult to get it to answer large aggregation queries in < 1s over multiple weeks worth of data.
The differences can look small, but they’re real and is what many ex-meta employees lament about when they say there’s no scuba equivalent in OSS.
I’m surprised none of the ex meta employees has build something here.
Honeycomb is by ex-meta employees and is the closest thing to Scuba.
I would even say, based on what glimpses of scuba i have seen here and there, that Honeycomb UX is mile above Scuba for this use case.
There was a lot in this that resonated with my experience at Microsoft, but there’s one bit near the end that needs amplifying:
If you have set up a company somewhere expensive (Silicon Valley, Seattle, London, wherever) and you require people to relocate there then you need to pay them for the cost of living premium. If you are allowing people to work anywhere in the world then there is no excuse for penalising the people who choose to live somewhere cheap (though requiring some minimal level of available Internet connectivity for remote work locations is fine).
Anything where you’re paying for input not output is a bad sign. The one that really annoyed me was that MS allowed you to buy a week of extra vacation time. People who take a healthy amount of vacation and don’t work long hours are vastly more productive than people who work for a long time without a break. The company is offering you the ability to pay them so that you can work more productively. This is totally the wrong incentive. This is why my contract at SCI, which is the template for people joining my team, has a requirement on the minimum amounts of leave people take, not the maximum. Smart people are far more likely to have their productivity suffer because they don’t stop working than because they take too much time off (for a lot of people I’ve worked with, the two weeks after they get back from a week off are far more productive than the four weeks before). Taking time off isn’t something that the company allows you to do as a reward, it’s something that the company requires that you do to ensure that you’re working at peak productivity. A tired person can introduce a bug in five minutes that it takes a week of debugging to fix. Managers need to be able to tell people that they’re working too hard and that they should stop for a few days.
I strongly agree with this. People’s cost of living can vary widely, and location is just one component. But we would never think it was OK to vary salary based on other lifestyle differences that affect those costs: whether someone is married or single, whether they have kids, whether they drive or use public transportation, whether they live in a house or an apartment.
I agree with your point, but I’d be willing to bet that there are a ton of people with kids who would totally support this. I mean, unpopular opinion, but parental leave is basically a bonus you get for making babies (not that I think it shouldn’t exist, but if there’s 8 weeks of parental leave, then everyone should be able to take 8 weeks off every few years or whatever).
I think the key difference is that parental leave is very much not “time off”. You are gonna work your ass off in that time, no matter what happens.
To add to this, for mothers who give birth, it is typically at least 6 weeks of medical leave. Giving birth to a child is a physically traumatic event. Being a male I have obviously never experienced it first hand but I have witnessed my partner go through it twice. The suggestion that maternity leave is anything even close to resembling “time off” is, frankly, borderline offensive (and the fact that in the US paid maternity leave is not even legally required is practically criminal).
Heh yeah unlike vacation time where people come back more productive, I can only imagine that parental leave is seen as “they’re going to be scattered and sleep-deprived anyway… and that’s not going to be good for anyone”
A long time ago, I worked in a web dev startup where both founders were absolutely adamant that employees take as much parental leave as they were entitled to. Both had worked in places where the opposite was true, both had ended up missing out on the earliest bits of their kids’ lives (and one of them would sometimes say that it had also put an unreasonable burden on his wife), and both had regretted it deeply. Neither wanted to foist that upon anyone else, ever. It wasn’t directly relevant to me since I neither have nor want kids, but it’s the kind of little thing that reminds you that there are workplaces not run by complete sociopaths.
The same place also deliberately tried to avoid the kind of “workday as LAN party” grind that burns out everyone but the youngest devs, and pushes out older people who have family lives.
It then got acquired by a larger company, and the previously very healthy company culture promptly went to shit.
That’s true, but it’s usually still by choice. If I decide to take 8 weeks of to help the local firefighters, should it get the same preferential treatment? Not an easy question.
I think at some point it becomes a matter of choice vs. right. Do you have the right to spend 8 weeks helping the local firefighters? Usually no, in most cultures, but honestly I think it’d be kinda nice. Do you have the right to have children if you want to? Generally it’s considered pretty important that the answer is “yes” in most places, so making it so that your workplace has to give you leave for it is pretty reasonable. As gpanders points out, a significant amount of it is medical leave.
What do you mean by “right”?
Legally speaking there is no law that forbids you to help the firefighters. There is also no law that forces the firefighters to have you join them.
In the same way, there is no law that forbids you to make children. There is also no law that forces your potential partner to help you with that.
To me that really looks like the same situation, legally speaking.
I think most people understand that some people have additional needs and that it’s okay, even virtuous, for society to compensate them for that.
Famously this idea is described as: “From each according to their ability, to each according to their need”
Now, I think everyone should have more holiday, but while we live in a system with limited paid time off then it makes sense to me that people with needy dependents should get proportionally more.
But people who live in a super expensive city literally need more. Having children is a lifestyle choice, just like living in an expensive city. I would take a different view if we, as a species, were critically under-populated. But since we’re not, I see no significant difference.
Edit: for something, like a disability, that is a condition, not a choice, I absolutely support accommodations, including additional pay and/or time off (whatever is helpful).
Raising children is labour and should be compensated. Extra paid time off for parents is part of that.
Also, for many people children are not really a choice. They’re accidental or it is unthinkable not to have them due to social conditioning, biological imperative, etc.
I’m not accusing you of this, but given our capitalist context, arguments that having children is a choice like any other tend to end up implying that poor people shouldn’t have kids, which is fucked up.
We could solve this as a society without prioritising parents especially by backing big transfers of income and political power to regular people.
I support this wholeheartedly. Making life better for everyone makes life better for parents (and maybe even disproportionately so).
Lots of people do realise they’re getting ripped off and that life could be better for everyone, but it won’t happen without more people getting organised. If you’re reading this and you’re not already in a feisty union or in some way building and exercising collective power then this is a good time to start, even if all you want is to protect your own quality of life.
I originally wrote a long sibling comment, but I’ll pare it down to a reply agreeing with and adding to your excellent comment!
Suppose that half of people in the US who have children had a choice, and that we want to accept their additional need as a reason for them to receive proportionally more. There are reasons over whose causes people have even less choice for which they might want to live in a city.
One reason might be that someone’s family has lived in a city for generations; do they have more an obligation to uproot their parents and grandparents to a lower cost-of-living area than someone has an obligation not to have a child?
Another reason might be that someone belongs to a minority group, in consequence of which, in defiance of reason but in light of their personal experience, they feel more welcome in cities than in lower cost-of-living areas. I can personally testify to this.
I think that having salaries that are not based on location is very reasonable. I think location-based salaries are also a decent compromise. I personally do not think that adjusting salaries based on the number of kids someone has or their identity etc. makes sense. I could see myself being swayed by new arguments.
Tell me you do not have children, without telling me you do not have children.
…and I think this is a very fundamental difference in different types of work. There’s design/project work, where you’re hired for your brain: deadlines are relatively distant, you build and design things, you make decisions and solve problems, and the quality and success of the result is very decoupled from the amount of hours put into it. And then there’s operations work, where you are hired for your output: not just flipping burgers or occupying a slot in a factory floor, but things like tech support, on-call or 24hr NOC/sysadmin work, working in construction or operating machinery, etc. In those your productivity is VERY coupled to the hours put into it: if you are present then the business is operating whether you don’t have anything or whether you’re working your ass off, and if you are not present then the business is not operating whether there is 1 Task Needing Doing or 1000. In those jobs a LOT of effort goes into scheduling people, and reliability and flexibility are valued much, much more highly by the business.
This is a distinction that most people don’t seem to know exists, for whatever reason. Nobody seems to talk about it but it very fundamentally shapes how the expectations of industry are shaped, which then shapes the laws and regulations and contracts and stuff that go into it. Maybe most people tend to work one or the other type of job their whole life?
I think there are two kinds of operations work, from your characterisation. If you are the receptionist or security guard, for example, there’s a huge difference between not doing your job at all and doing it badly. The gap between doing it badly and doing it well can also be large but, in normal circumstances, it is less than the complete shutdown that happens if you do nothing. In contrast, SRE-type work often involves doing the kind of things that, if you don’t do them until tomorrow then it’s not a disaster but if you do them wrong can result in data loss, reputation loss, and even penalty clauses for a customer.
Even this isn’t so clear cut. A receptionist who is overstressed and snaps at the wrong person may lose a million dollar contract.
The thing that surprised me recently was reading productivity studies on construction workers, which showed exactly the same shape curves as for knowledge workers. It turns out that making mistakes from from tiredness that offset benefits from working more is pretty universal.
Oh, it’s certainly not a clear cut division. But in my experience the differences between the two are much bigger than the differences within the two. They result in very different mindsets for me.
Oooooh, interesting! Not surprising, but not entirely obvious. Do you remember where I can find those?
I’ve heard something similar from the local railway company, where (in the IT development department) you get more free time for a cut in pay..
A friend of mine sounded very happy about it at the time, you make a compelling argument against it. At the very least, the burnout that might result should be something that costs the company a lot more than what it saves by cutting pay.
Everyone one wants US salaries but nobody’s willing to give up all the local benefits (5 weeks paid holidays, 35h per week, strong worker rights, nationalized retirement, free healthcare…)
On the other hand, if someone is happy to work for $50k because they’re 12 timezone hours away and live on a beach in a $10k bungalow and they have full time domestic help for a few thousand bucks a year, I don’t know why a company should hire them at $500k just because a few other employees live in Palo Alto.
Companies will only pay what they have to pay. That’s how free markets work.
It gets more complicated when it’s an employee’s choice to move from Manhattan to Vietnam, of course. Should the company reduce their pay just because the cost of living for the remote employee has dropped? That seems creepy.
There aren’t that many highly paid jobs in which you can just choose to live anywhere. Programmers are pretty lucky.
Markets will eventually price goods based on both supply and demand. Prices go up if demand outstrips supply and up if supply outstrips demand. The company offering $50K is usually not the only source of demand.
If you make this person a $50K offer, then another company can offer them $100K and they’ll be really motivated to take it and that company is still saving money relative to hiring someone for $500K. Retention is usually cheaper than hiring, so offering a salary that encourages people to leave is a bad long-term strategy. We’ve seen this with Indian outsourcing companies: their competent workers rapidly get hired away at higher salaries and so they’re left with inexperienced or incompetent people (or they have to match international salary rates).
If you’re hiring remote workers anywhere in the world, then you’re competing for talent not just with companies local to that person but with any other company that offers remote work. If you have someone who you’re paying $50K, they don’t have to offer $500K to steal them, $100K is fine. But then someone else comes along and hires them for $200K, and then someone else offers $300K. And they’re still cheaper than those $500K folks, so the ones in the bay area are getting fewer offers. If you’re getting $500K of value from someone doing the job, then it makes sense to offer $400K to hire someone talented away from the competition. If you’re getting $1M of value from someone, then offering $500K anywhere in the world gives you a much better pool of applicants than offering $500K to people in the Manhattan and $50K to people in Vietnam.
Salaries probably won’t eventually converge on the current bay area rates, but there are a lot more smart people that don’t live in the bay area than do and an increasing number of companies competing for their labour. When I was looking for a job last year, I got several pings from companies that do full remote and don’t do location-based salaries. If you do, you’re probably not competitive as an employer.
I’ve hired a few thousand people across 6 continents, and (unfortunately) had to lay off some as well. Everything that you wrote in this response is correct in my experience. I’m not arguing for or against, or defending, or promoting. Like what you wrote here, I was simply pointing out reality. Reality doesn’t ever seem to care what I think about it.
I have always paid well when I hired people. But when hiring someone in Prague or New Delhi or St Petersberg, I wasn’t typically offering Bay Area rates. I may have paid better than what most people in Czech Republic or India or Russia make, but as you suggest, I was offering based on the market prices and how badly I wanted / needed a particular employee, and how badly other companies wanted them. I’m super proud to have had a lot of employees targeted by other companies for recruitment, and super happy for their successes after leaving my teams. I always tried to make it hard for other companies to poach, but good people are always worth paying dearly to have, and it’s also good for people to get to try out some new challenges from time to time.
What’s the largest application written in Gleam? I’d love to take a look a non toy code base to get a feel for how it all works together: language + ecosystem + deployment.
I’m not sure what the largest open source one is. I mostly know of smaller ones such as https://github.com/gleam-lang/packages
I suspect this is not working as intended.
Hah, I opened post on Lobster just to say this 😂
The whole article has a strong LLM vibe: the prose is very verbose, and code mistakes such as this one happen a lot with LLMs.
Well that’s a first for me, getting called an LLM. :) I can assure you I wrote it all, but point taken. Here I was worried that the prose was too short! Guess not. The code mistake I can fix, I’ll make it more clear that it’s not a full and complete function. I thought that was obvious – which it is as y’all have noticed, but it’s clearly sending the wrong vibe
FWIW, If you want to see the working, actual code (not from the blog), you can look at https://github.com/openziti-test-kitchen/appetizer/blob/main/clients/reflect.go
I can understand the comment. Maybe I should remove the ‘main’ function so that it doesn’t look like a full and complete function or somehow indicate that elsewise… I can perhaps add a comment at the top, or a function call to some function that returns the context… I tried to leave out the extra code like that but probably should update it to make that more clear, thanks
I hope you’ll find the time and energy to keep on writing these. It’s a pleasure to read. Nice work on Sophia, it’s a neat small language.
Not sure if you’re open to feature requests, but exposing host functions, similar to wasm/lua/etc. would be great. One could write most of the logic in Sophia but hook into the extensive go ecosystem when needed.
Im always open for feature request, you can simply create an issue for one :). I would know how to implement that but i will do some research about it.
Anything by Zhang is worth a look IMO.
Normally I don’t submit these small blog like posts here but
pagefind
was so easy to setup and integrate in my own static site generator and looks beautiful. It even defects images, the default UI looks nice and it responds fast. I think there are more people here that run a static site and would be interested, including an example of how it looks with 10+ years of static site content. I just recently heard about pagefind, it was discussed here almost a year ago ( https://lobste.rs/s/pvkg80/static_low_bandwidth_search_at_scale ) but I missed that…Direct link to save a click: https://pagefind.app/