I've been wanting to use Crystal. Does anyone have experience running Crystal in production? Without getting into the specifics, the Go parts of my personal project are a websocket server that upgrades when a user is authenticated and authorized, and their actions spawn jobs in a queue. I have processors (written in Golang) that take these tasks and perform somewhat complex async actions. Would Crystal fit the bill here? I'd really like to avoid getting into Elixir/Erlang world or any kind of interpreted language that will complicate the deployment process (like Deno or Ruby).
I want off Mr. Golang’s Wild Ride (2020)
241–250 of 477 posts
Re: I want off Mr. Golang’s Wild Ride (2020)
#242Earlier quoted context omitted.
> Note that there definitely are PL communities that generally can't handle any criticism irrespective of civility, but the Go community isn't among them. Indeed, in my experience, Go's critics are very often much more zealous than its proponents. This is because Go is a programming language for people who don't care about programming languages. I mean this in the most positive way possible. If you're using Go, it's…
> Go is a language that gets out of your way, encourages you to solve your problem Maybe I'm just too dumb for Go, but this is not consistent with my experience at all. Go's insistence on pretending that complexity doesn't exist would get in my way all the time. Go's extreme hostility toward FFI calls got in my way several times.
Your complex programs aren't easier in Go, in my experience. The simplicity of the language doesn't help me much when my day is spent fighting to figure out how to make a problem which is inherently complex easy to maintain, reason about, and be without bugs.
I want a language that makes my day simpler. Where at the end of the day, my "net complexity" is less. Go leaves all the complexity to you and offers you very few tools to solve this. Bugs, spread out logic, and even runtime costs of the overuse of Interface{} (prior to Generics at least) left me with a lot of things to solve myself. My days in aggregate were more complex with Go.
Just my experiences.
Re: I want off Mr. Golang’s Wild Ride (2020)
#243Earlier quoted context omitted.
I mean I spent quite a few words talking about how there's a happy optimum where beyond that you start to get too much magic and code gets too terse and unreadable. You just did prove my point though which is that this is the only argument that Go programmers consider, and they blindly reject that adding more lines of code can harm readability.
> You just did prove my point though which is that this is the only argument that Go programmers consider, and they blindly reject that adding more lines of code can harm readability. Can we lower the rhetorical temperature a notch? Just because someone disagrees with you doesn't mean they're "blindly rejecting" your reasoning. In particular, I'm not just a Go programmer--I've used Java, C#, Python, JS, C++, and C in…
Re: I want off Mr. Golang’s Wild Ride (2020)
#244Earlier quoted context omitted.
I think it's a matter of different goals. What you describe is pains of a very senior developer who has worked on a lot of very subtle bugs and never questioned their desire to deliver the best software possible. Parent comment, however, talks about a situation where you have to hire dozens (if not hundreds) of $10/hour developers to ship software that is just good enough. I mean folks who may be great people and des…
I'm not fond of this framing, which suggests that go is for bad programmers and rust is for good programmers. If Go makes it easier for bad programmers to write decent code, it also makes it easier for good programmers. Good programmers aren't good because they're insanely clever and whip up brilliant combinations of abstractions. They're good because they write maintainable, understandable, simple, and effective cod…
Re: I want off Mr. Golang’s Wild Ride (2020)
#245To say go's simplicity is a lie, then to go and and say this; > This function signatures tells us a lot already. It returns a Result, which means, not only do we know this can fail, we have to handle it. Either by panicking on error, with .unwrap() or .expect(), or by matching it against Result::Ok / Result::Err, or by bubbling it up with the ? operator. What the hell? I'm not a rust dev. I have no idea what half of…
What's your argument? The point the article is making is that Go says it's simple, but isn't, while Rust doesn't say that it's simple, and uses its complexity to solve the complex problem. Bubbling up means returning the error to the caller, and is general error handling jargon.
Re: I want off Mr. Golang’s Wild Ride (2020)
#246Earlier quoted context omitted.
> Note that there definitely are PL communities that generally can't handle any criticism irrespective of civility, but the Go community isn't among them. Indeed, in my experience, Go's critics are very often much more zealous than its proponents. This is because Go is a programming language for people who don't care about programming languages. I mean this in the most positive way possible. If you're using Go, it's…
> Go is a language that gets out of your way, encourages you to solve your problem Maybe I'm just too dumb for Go, but this is not consistent with my experience at all. Go's insistence on pretending that complexity doesn't exist would get in my way all the time. Go's extreme hostility toward FFI calls got in my way several times.
Re: I want off Mr. Golang’s Wild Ride (2020)
#247Earlier quoted context omitted.
It requires several additional lines of code just to bubble up an error, for starters, and there's nothing stopping you from ignoring errors and continuing with what could easily be corrupt data.
The latter is a much stronger argument than the former (no idea why people get so worked up about character counts), but even then, "shit" is really strong considering how often one experiences exception traces when using an application written in Python or Java or some other exception-based language. Point being, we should probably evaluate error handling schemes based on results rather than ideology (even though I…
Think of reading code as mining ore. If the ore is rich, you don't have to mine and process nearly as much of it to get the material you need. If the ore is poor, you have to invest extra effort to mine more ore to get the same amount of refined material.
You might think Go is easy to read because lines are individually very easy to read, but Go code is so information-poor (partly because of error handling boilerplate) that you have to read a lot more lines of it to understand what a system does compared to other languages. Quantity has a quality all its own, and Go does bog you down with its sheer line count. I'm not one who often appeals to this argument, by the way; the only other language I've done significant work in that I would apply it to would be C. It's very common to write bloated, information-poor Java code, but that is still a choice, even if it is the most popular one.
My reaction looking at Go initially was that it was exciting to have a fast, simple language designed for writing services. My reaction to reading and writing code of real applications has been that Go is badly suited for writing nontrivial application logic.
> how often one experiences exception traces when using an application written in Python or Java
Python and Java aren't particularly ambitious standards for a 21st-century language.
Re: I want off Mr. Golang’s Wild Ride (2020)
#248Earlier quoted context omitted.
Well, if you don't know the structure of a resource ahead of time but know that it has a status.ready, I would think that would be a candidate for a generic? I haven't explored that much yet, but in retrospect I might even be able to convert all objects to a struct that has only status.ready without generics. I've only been in the ecosystem 6 months, but yeah larger abstractions are difficult too. I'm not a fan of th…
Go's equivalent of sub-classing is embedding
For example in the base struct I had an interface and a ton of methods that use it.
Then when I declared the concrete struct, I have to manually point the concrete type that matches that interface to the base class's interface.
Composition doesn't really allow the same thing as inheritance. Composition typically means you'll have a motor and wheel struct in your car struct and maybe your car struct uses both in some drive method.
Inheritance is more like having a car struct with a rev engine method, but no concrete engine set.
So you can later make a Kia, set the motor to a type, and then call the base struct's rev engine method.
Again, it's not impossible, just really ugly.
Re: I want off Mr. Golang’s Wild Ride (2020)
#249Earlier quoted context omitted.
> That would be great if Go provided better performance. With its awful FFI, you have no recourse when you hit its limits other than to rewrite the entire codebase in something else. I wouldn't know. I've never run into an issue where Go's performance was a real bottleneck, and anyway every mainstream language with easy FFI still has significant FFI overhead (so much so that many programs actually run slower with FFI…
> every mainstream language with easy FFI still has significant FFI overhead (so much so that many programs actually run slower with FFI). This isn't really true for Rust This isn't true for almost any language to the extent it's true for Go, and for many compiled languages it isn't really true at all. > Try building a significant Python project on anything except a recent version of RHEL, Debian, MacOS, or Windows.…
Go's object structure is much closer to C's. In many cases, it's just casting pointers (e.g., you can convert a Go slice to a C array by taking a pointer to its first element and casting it to a pointer of the C element type, provided the element types are binary-compatible). This means fewer allocations than a language like Java or Python where you would have to allocate a new slice and chase pointers around the heap for every field in every element in the array. In my experience, most of Go's overhead comes from function call bookkeeping at the FFI boundary rather than marshaling data.
> for many compiled languages it isn't really true at all.
I'm pretty sure it's still true for many compiled languages (e.g., Java, Haskell, etc).
> While these are legitimate theoretical problems, none of them are really problems in practice.
They were problems for me in practice.
> Containers don't need to be scratch, and if you're building a Python project, you're already not running a scratch container, so the addition of FFI doesn't change that.
Containers don't need to be scratch, but they often need to be lightweight with low cold-start latencies (including pulling). Not having to pull in a whole distro is advantageous. And a pure-Python project certainly could run in a scratch container (i.e., a container with just the interpreter, the program, and the program's transitive dependencies).
> Nix is not an environment I've ever seen a requirement to support, let alone had a requirement.
"Reproducible builds" is the requirement. Our customers' security teams vetted our dependencies individually, and if a dependency changed we would have to have that dependency re-vetted.
> It really seems to me that you like Go and you like Go's design decisions, but "I like this" is not the same as "this is better than that".
Clearly, no one here is conflating those things. I'm arguing that Go has a particular strength, not that that strength is the only factor.
Re: I want off Mr. Golang’s Wild Ride (2020)
#250Earlier quoted context omitted.
> Errors as return values is only acceptable for code that is so performance-sensitive that you aren't allowed to do dynamic memory allocations Not really. It is acceptable for code whose maintainers value readability and simplicity over everything else. I totally agree that readability and simplicity are quite subjective and this is up to the maintainers. I don't really know what "conditions+restarts" is but a few a…
It's probably sufficient for this conversation to just understand it as try-catch. A function is invoked; if it "signals" (throws) then control moves to a handler that matches the signal (exception); the handler runs and resolves the situation. Of course, Lisp being Lisp, the system is extended to announcer voice FULL. GENERALITY. but in its simplest form it's basically equivalent to exception throwing.
However, the difference between a try-catch and conditions / restarts is that when one signals a condition (exception), the restart (catch) has a continuation from the condition. This allows you to inject an expression into the location where an exception occurred and "restart" your code from that point.
Whether you do such a thing or not depends on the code, on the type of condition raised, and on what expressions are valid. So you get a lot more flexibility in how errors are handled across the system. But likewise: more complexity in having to make that choice in the first place.
Going farther than this, conditions and restarts are really just a fancy way of packaging delimited continuations. I don't personally know any non-Lisp language that has attempted to package these concepts (maybe Dylan, which is a Lisp-like in its own way but without the syntax?). Going back to the original thought regarding error handling - I think Result type handling is fine and that most languages would be better served by that than having different types of exceptions. Conditions and restarts are powerful but your language has to be very expression focused (i.e. does not use a lot of statements) and it's not really clear that there's been a lot of work on making restarts nice to use. Exceptions in all languages that have them have their own set of associated problems, for what its worth, and it's not as easy to move Lisp features into a non-Lisp as one might believe...