Readit News logoReadit News
BillyTheKing · 2 years ago
I've been mostly writing Typescript the past 3 years - and recently started writing code in Go. Initially I was a little apprehensive, lack of array functions, slightly less flexible type-system, etc.

But after spending some time writing Go I now had to re-initialise a typescript project for a small-ish team (4-5 devs). The amount of time spent on things such as linting, selecting the correct library for server routing, the correct server, coding standards, basic error-handling and enforcing it with a custom error or Result type to get out of this nested try/catch hell which still loses the majority of errors. Setting up testing and mocking. Setting up Prisma and what not - and finally the PRs are still a hit and miss, some ok, some make use of weird JS functions..

Don't get me wrong, I really do like Typescript. But I gotta say after all of that it's just great using a language with a fantastic standard library, proper type-safety, with some coding standards built-in. It's obviously not without quirks, but it's pretty decent - and great to see that routing has now also moved into the standard library, another bit that you don't have to worry about - can't wait for some map/filter/find slice functions though!

KingOfCoders · 2 years ago
In my CTO newsletter I recently wrote about TS vs Go releases, with Go 1.22 as an example. TS gets more and more complicated with each release, catering to the power users. Go adds things that makes it simpler to use, like the (missing) range over integers. It's like game sequels, they add more and more canon and game mechanics, then game sequels (or comics) need to reset to make them more accessible to newcomers again. Programming languages can't do this, so I'm happy that Go keeps this in mind.
simiones · 2 years ago
All languages that keep evolving get more complex over time. The changes are always intended to make programs written in the language simpler.

Go moves at a pretty slow pace, adding only minor new features (and thus minor complications) in most releases. Even in 1.22, they are previewing a new feature, range-over-functions, which seem to be basically C#/Python's iterator functions - a feature which will, of course, complicate the language - but make certain programs simpler.

As a general rule, the more features a language has, the shorter program that implements a particular algorithm can be, but the harder it is to learn, and the bigger the chance that it will be misunderstood. There are exceptions where certain features make languages more verbose (e.g. access modifiers), but typically only in minor ways.

pjmlp · 2 years ago
The thing with Typescript is that it is only a fancy JavaScript linter, so the only way to justify newer releases is to keep adding up the type system, there is nothing else when language features that aren't type system related are supposed to come from JavaScript evolution.

So they either say they are done, or keep adding type theory stuff until it implodes, I fear.

Actually I am looking forward to type annotations in JavaScript now in the roadmap, being good enough for general use cases.

ken47 · 2 years ago
Yes, but TS users can stay on the "type-newbie" path, which is still a huge improvement over vanilla JS and doesn't take much effort. What I've had issues with is devs who came from the vanilla JS world and love it, so they go out of their way to avoid utilizing more complex types when they would add no-cost safety (aside from the initial minutes or hour spent learning the feature).
jhanschoo · 2 years ago
> TS gets more and more complicated with each release, catering to the power users.

My understanding about the use of advanced/more expressive TR features is that it's OK if you don't use them, and don't bother wasting time for most products. Bot if you are writing a library of framework in TS, go ahead especially since they are meant to improve the experience of consumers.

falsandtru · 2 years ago
They just keep adding new features for fear of losing their position because they can't decorate the release notes. MS doesn't give high marks for bug fixes. Thus the bugs keep growing.
cookiengineer · 2 years ago
I feel very similar to your experience.

What made me stay in go is its amazingly unified build toolchain. The things you can do with go:embed and go:generate blow my mind in every other project.

The golang.org/x package is also another thing, where there is pretty much every internet RFC related implementation available, ready to use.

avtolik · 2 years ago
Can you give some examples how are you using go:embed and go:generate?
hambos22 · 2 years ago
> can't wait for some map/filter/find slice functions though

Till that day comes, you could use the "lo" library (inspired from lodash). It's my goto Swiss army knife for golang projects.

https://github.com/samber/lo

puika · 2 years ago
On the other hand, I advise you NOT to use this kind of library and write simple, fast go code most of the time, with the occasional generics helper. Why the hell would I clutter my code with, for example: https://github.com/samber/lo?tab=readme-ov-file#fromentries-...
Cthulhu_ · 2 years ago
Only if you're willing to take the cost associated with that; it adds a "DSL", an extra language to the language, reducing its goal of simple and readable code. Compare also with using a testing library that adds human-readable assertion phrases (expect(x).toBe(y) etc).
stpedgwdgfhgdd · 2 years ago
Why not use the generic-based built-in slices?

https://pkg.go.dev/slices

ryloric · 2 years ago
Yeah, it's quite productive in a counter intuitive way, not having a ton of features just removes a lot of tiny decisions you have to make in a richer language.
lnxg33k1 · 2 years ago
I’ve worked for a while at a client using typescript, after a while I started calling it “Tricks Driven Development”, every time I had to do something I’d read the docs, but then someone would communicate some trick not on the docs that was possible to use
leonheld · 2 years ago
Go is beautiful, productive, readable. I love Go. It's my "C with niceties".
deergomoo · 2 years ago
I feel the same way. I was recently evaluating TypeScript and Go for a small project at work, having little experience with either. I went with Go almost entirely because I’d made decent progress in solving the problem by the end of my timeboxed investigation period. After a similar time with TypeScript, all I’d really done was get bogged down trying to work out what tooling I should be using.

Compilation, testing, and automatically formatting TypeScript all have multitudes of options with their own pros and cons. All that stuff is just built into the Go toolchain. Yeah it’s not always perfect but it’s more than good enough and, importantly, it’s ubiquitous. There’s nothing to think about or argue over.

That said, I’ve been wanting to try Deno. My understanding is that it takes a much more Go-like approach to running TypeScript.

fennecbutt · 2 years ago
Yeah someone I know has been banging on about go, too. I have a personal project I want to do so maybe I'll pick go for it.

The main pro and con to the JS ecosystem is the fact that it's so flexible; you can mostly do things how you want and there are so many libraries in each space all competing. But because you can do anything how you like it becomes hard to decide which library you want to use (or a diff one every project) and every dev has a different way of accomplishing the same thing.

I used to write internal libraries and frameworks a lot in my career and one of my mantras was to use TS to try to lock devs into doing things a certain way but there only so far you can go.

My main worry with Go is like, is everything built in? Are there multiple say, Web server libs like with nodejs or only a single option? How are deficiencies addressed if there are fewer options and fewer ways og doing things?

Touche · 2 years ago
Go is the language for getting sh*t done.
WuxiFingerHold · 2 years ago
Best summary of Go. That should be the headline of the Go website.
WuxiFingerHold · 2 years ago
Interesting ...

>> The amount of time spent on things such as linting, selecting the correct library for server routing, the correct server, coding standards,

I don't fully agree here. Those points are pretty straight forward and coding standards (not formatting, for TS prettier is pretty standard, btw.) need to be defined even in Go projects. Also, Deno has much of the setting up solved.

>> basic error-handling and enforcing it with a custom error or Result type to get out of this nested try/catch hell which still loses the majority of errors.

Fully agreed. Maybe I have a big lack of understanding of error handling in NodeJS, but how on earth do I find out what functions can throw errors and what are the errors thrown? If someone could enlighten me I'd be really grateful. To be on the safe side I would need to run my whole code in a try..catch block. How to decide how to handle different errors if I don't know which errors can occur? On the other hand, just yesterday I had to debug a Rust panic in a smallish code base. Even with stacktraces turned on it took half an hour to find out where there error occurred. Still, Go and Rusts error handling is much better. IIRC, in Java you see all types of exceptions that can occur in the docs of a function?

>> Setting up testing and mocking. Setting up Prisma

Again, not a big thing IMO. If you like an ORM, Prisma is one of the best.

>> Don't get me wrong, I really do like Typescript.

Yeah, that's the thing. Typescript is such a fantastic language. Writing Go feels like using a hand-axe. Typescript's null handling alone makes it 10 times better (I hope everyone is using it, but that underlines your point regarding the conventions needed ...). I recently found a lib that gives us compile time checked pattern matching like in Rust.

endtime · 2 years ago
I got to spend a couple years writing Dart (not Flutter) and found it to be the best of both worlds. Such an underappreciated language.
tonis2 · 2 years ago
Yeah, Dart is way better / easier to debug, than Typescript in my opinion
WuxiFingerHold · 2 years ago
Interesting, Dart for backend? Do you build APIs? How is the ecosystem? I heard good thing of Swift as well, but I have concerns that it's to niche for backend stuff.
baby · 2 years ago
The problem of typescript is the lack of convention and the emphasis on configuration, the reverse made Golang a great language.
stokedmind · 2 years ago
Other replies miss the point - the problem doesn't lie with Typescript itself exactly. Setting up a nodejs/js project with all of the fixings (linting, Typescript, spell checks, builds if needed, etc) is quite tedious.

Sure you can accept some template project or CLI tool to kickstart things if just starting out, but at some point you will need to tweak the configuration and there is an enormous realm of options.

I'm surprised no one mentioned this already, but a runtime like Deno goes to great lengths to solve alot of these pain points. You get testing, linting, bundling, and Typescript out-of-the-box with sane settings. If Deno worked better with GRPC I'd probably be using it right now in my work projects!

38 · 2 years ago
> can't wait for some map/filter/find slice functions though!

just use a for loop. 90% of the time the code will actually be clearer, and faster.

the hoops people just through to avoid 2 extra lines of code is mind boggling.

shepherdjerred · 2 years ago
Deno fixes most of the issues you're describing.
__loam · 2 years ago
I love go. It's just so simple.
nalgeon · 2 years ago
If you find the official release notes a bit dry, I've made an interactive version: https://antonz.org/go-1-22
justinclift · 2 years ago
Cool, that does help. It wasn't immediately obvious what the problem with the for loop sharing thing meant, but seeing it run and give unexpected results helped. :)
thatswrong0 · 2 years ago
Thank you this is awesome. I find it so much quicker to read and understand things like this with examples.. and these are runnable / editable! Sick.
scns · 2 years ago
The Compact and Replace examples leave me puzzled, are they correct?
nalgeon · 2 years ago
They are correct.

> Functions that shrink the size of a slice (Delete, DeleteFunc, Compact, CompactFunc, and Replace) now zero the elements between the new length and the old length.

yeonsh · 2 years ago
Oh, it's great.
drewlesueur · 2 years ago
really cool
divan · 2 years ago
I've been writing Go for 9+ years, but for the last 4 years, I had to write a lot of Dart (for Flutter). I consider these two languages to be on the opposite sides of the complexity stance. Dart tries to add and implement every feature possible, but Go is the opposite.

Two observations:

1) I'm spending a lot of time fighting multiple ways to init stuff in a class (i.e., declare the variable and set the value). Depending on the final/const/static/late prefix, there are multiple ways to init it in the constructor or factory or initState() method of Flutter's StatefulWidget, and god forbid you to refactor any of that – you'll be forced to rehaul all the initialization. Dart's getter feature (which makes functions look like variables) also adds confusion, especially with a new codebase. I constantly find it embarrassing how much time I need to spend on such a straightforward thing as variable/field initialization. I often find myself missing the simplicity of Go.

2) Compared to Go, Dart has all the juicy stuff like maps/streams/whatever for packing all in a single line. It's very tempting to use those for quickly creating singleliners with hard-to-understand complexity. Sometimes I even start feeling that I'm missing those in Go. But when I get to debugging those or, especially, junior developers looking at these write-only singleliners, with an array of ugly hacks like .firstWhereOrNull or optional '(orElse: () => null)' parameters, they are very confused, especially when cryptic null safety or type errors stops them. Rewriting those singleliners as a simple Go-style for loop is such a relief.

Jean-Papoulos · 2 years ago
Tip about Dart : Don't use initState for stuff that doesn't directly concern UI (setting the hint of a text field, for example).

Most of my Flutter pages rely either on having very few things to do, or having a MyPageController object such that the parent creates a controller, initializes it however it likes and the child page's behavior is dependent on that controller. A typical example of this would be the parent being a page containing a list, and the user wanting to edit a list element. Create a controller, give it the element, and send the controller to a child page where the user does the editing. On return, the parent can look at the element or other variables/callbacks in the controller to decide what should change in the UI.

This also allows finely-controlled interactions between widgets without having to delete with InheritedWidget or the likes. Of course you should use a state management library with this, even though a lot of the time I don't.

divan · 2 years ago
Some people joke that state management in Flutter is like http mixers/routes in Go. But I think it's much worse :)

Granted, UI state handling is not an easy task, and it's not directly related to the topic of complexity of the languages. I had an article written a few years ago about a thought experiment of Flutter being implemented with Go. It's a bit naive, but still fun to think about. [1]

[1] https://divan.dev/posts/flutter_go/

doctor_eval · 2 years ago
I agree, Go’s simplicity is the best thing about it. I think the same thing about ranges, I think I end up typing the same number of characters - but spreading it over three lines makes it feel “longer” than a comprehension like “forEach(f)”. But then I write the range longhand, and it’s no big deal.

Speaking of initialisation though, I do wish Go had an idiomatic way to initialise struct fields to specific values. I don’t care about the lack of constructors; mostly, I just wish I could have bools initialise to true sometimes.

divan · 2 years ago
Sure, it's always a tradeoff. Yet my pet peeve is that people rarely talk about the social aspects of the programming language. It's called "language" for a reason. We express our thoughts using this language ("I intend this code to do such and such"), and we expect other people to be able to understand what we intended, and we want to make sure that they understand exactly as we want them to.

I judge languages on their ability to collectively construct mental maps in the brains of the developers who work on the same project. If they all read the same code, will they be able to understand the task and intent of that code without additional explanations? How cognitively hard would it be?

Gottfried Liebnitz was obsessed with finding a Universal Language, which was exactly about this – making communication clear and lacking misunderstanding. I feel like Dart (and most other languages) approach is the opposite of that. Creating multiple ways to express the same intent is a sure go way to introduce misunderstanding and fracture the speakers of that language into dialects and groups. Go's, on the other hand, is really good at making this "reconstructing mental map" part a joy.

bradleyishungry · 2 years ago
initstate is an override of statefulwidget, its no different than any other frameworks lifecycle events and its a flutter thing, so its not really something you would confuse with a constructor or something. I can understand not knowing if you should create a normal constructor or a factory i guess, but getting confused about initstate is not a reason go is superior.
erik_seaberg · 2 years ago
We should be optimizing for expensive experts’ ability to communicate concisely with each other. Keeping the power tools of syntax out of novice hands just deters them from developing expertise.
saclark11 · 2 years ago
> When io.Copy copies from a TCPConn to a UnixConn, it will now use Linux's splice(2) system call if possible, using the new method TCPConn.WriteTo.

Interface upgrades, yet again, transparently giving us more zero-copy IO. Love how much mileage they’re able to get out of this pattern in the io package.

lifthrasiir · 2 years ago
Go standard library is absolutely littered with these kinds of tweaks. Not necessarily bad, but it does make an integration with non-standard library less satisfactory because such special casing is not scalable. Still within the expectation though.
tommiegannert · 2 years ago
> Enhanced routing patterns

> This change breaks backwards compatibility in small ways, some obvious—patterns with "{" and "}" behave differently— and some less so—treatment of escaped paths has been improved. The change is controlled by a GODEBUG field named httpmuxgo121. Set httpmuxgo121=1 to restore the old behavior.

That's a great enhancement now that the future of Gorilla Mux is shaky. But why doesn't that go against the Go 1 compatibility promise?

> It is intended that programs written to the Go 1 specification will continue to compile and run correctly, unchanged, over the lifetime of that specification.

EdiX · 2 years ago
The way they are using to route around the Go 1 compatibility promise is to gate these backwards incompatible changes on the value of the go directive in go.mod. If it says 1.22 or later you get the new library behavior, otherwise you get the old one. We'll see how well this ends up working in practice.
lloeki · 2 years ago
This mimics Perl's "use 5.x", although the latter is scoped per module. Perl's backward compatibility track record validates the soundness of this approach.

Perl can also selectively enable features, in a way not dissimilar to Python's "from _future_ import X", except the latter is about forward compatibility with a future default, whereas Perl is all about backwards compatibility as a sane default.

I guess Go does it at the mod consistent level because it needs a global view of features whereas Perl can dynamically alter itself (including its parser) live.

zaphodias · 2 years ago
Usually the Go team scrapes from GitHub and open source programs how people use something before breaking them; I suspect they found little usage of { and } in HTTP handler paths. They also provide a way of opting out the new behaviour, so they don't force you to change anything in your code (but yes, it does require you to set a new env var).

The change to the for loop semantic is another example in this release; it effectively is a breaking change.

All Go programs continue to compile and run, though with minor behavioural changes. I think Go took a pragmatic approach, and that was one of the reasons for its success.

carstenhag · 2 years ago
I don't see the point of using semver (or at least telling us about the same guarantees) and then not making use of it.

If there were 10 breaking chances we should be at 11.x now, not at 1.x with 20 environment variables.

maccard · 2 years ago
I agree this is a (rare) mistake with Go. If they're going to break versioning using go.mod, they should at least break it in a way that makes it better. I'd much rather have my code fail to compile when we change go.mod to 1.22 than have to detect subtle runtime issues like this.
fbdab103 · 2 years ago
Because then they would lose their pithy advertising slogan. Minor or not, it is a non-compatible change.
omoikane · 2 years ago
> In Go 1.22, each iteration of the loop creates new variables

This was previously discussed here:

https://news.ycombinator.com/item?id=33160236 - Go: Redefining For Loop Variable Semantics (2022)

mjw_byrne · 2 years ago
Perhaps I'm a dinosaur but I don't like the range-over-function addition. I don't think it adds enough convenience to justify the complexity it adds to the language, and the functional style feels at odds with Go's explicit, imperative (albeit verbose) and feature-lean style, which I think was one of its major strengths.

For the same reason I think the range-over-integer feature is a misstep. Go's lean feature set and minimal mental load have always been major strengths and differentiators of the language.

Cthulhu_ · 2 years ago
So at the moment this feature is optional / experimental and opt-in; if they make it standard on, I hope they add an opt-out mechanism of sorts, so that developers are discouraged to use it if it's not commonplace.

I find that one big problem with software developers is keeping developers from adding complexity, or "flexing". Especially developers earlier in their career, present company included, tend to overcomplicate a solution instead of just solve the problem in a straightforward albeit inelegant and wordy fashion and move on.

mjw_byrne · 2 years ago
Yep, one of the big wins for Go was preventing this by just not supporting complicated, "clever" code. It doesn't have a ternary expression, "i++" is not an rvalue, you cannot pack a huge amount of work into a single line, you can't overload operators or even functions and so on.

The opposite extreme is arguably C++, which I personally quite like (probably because I use it only for solo projects and don't try to collaborate with anyone), but I can't deny that it's an awkward, gnarly monster of a language. It'd be awful to see Go end up like that.

lenkite · 2 years ago
I also think this feature feels premature. Ideally, it should be introduced after lambdas and generic parameter packs. The Generics support in Golang is not sophisticated enough to support this at the moment leading to the strangely imposed limitations
jjice · 2 years ago
> Ideally, it should be introduced after lambdas

Like syntactic sugar over `func`? Since func can already be anonymous and passed around just fine, I don't expect them to add additional syntax for functions.

ericpauley · 2 years ago
I'm ambivalent as well. It doesn't even save lines of code compared to passing an inline function.
peter_l_downs · 2 years ago
The addition of `sql.Null[T]` is great. I'll probably start using that in new projects. In current stuff, I'm relying on sqlboiler's null [0] whose API is very similar — it works the same way as `sql.Null` will, but has an additional `IsSet() bool` method that tells you whether or not the value was ever explicitly set, to help you distinguish "intentionally null" from "null due to uninitialization". (Sounds nice, but I've never once used it.)
eweise · 2 years ago
Curious when you need to know when null is intentional or not. There's no corresponding type like that in the db.
buzer · 2 years ago
For example when you are using partially populated record to update the database. If field is null intentionally it means it should be updated to it. If it's not set the update statement should not touch it.