Hacker News new | ask | show | jobs
by noncoml 3147 days ago
Things I love about go:

1. Probably the best ecosystem out there.

2. Go routines

3. (Enabled by (2) actually) `defer`

4. That I can add interfaces implementations to structs I don’t own

5. No exceptions.

Actually (5) is one of the few things I don’t like about Haskell.

If Go had ADTs and generics it would easily be my favorite language.

Edit: and of course the channels.

Edit2: yeah, i have no idea why i connected (2) and (3). Had just woken up. No idea what I was thinking.

11 comments

The best ecosystem out there? Since when? I'd say that Java and Python have huge, wonderful and full ecosystems. Golang doesn't even come close to that, yet. Furthermore, Golang doesn't even have a community standard (or several standards) dependency manager.
Python's packaging is a nightmare of half-baked, incompatible approaches that puts the lie to the famous "Zen of Python" that "There should be one-- and preferably only one --obvious way to do it."
and yet Pipenv is miles ahead of what Go has to offer.
And yet pip has just worked for me. Can’t say the same about any of the go dep tools.
Try using pip to install multiple versions of the same library at the same time, or the same library running under different versions of Python.
Agreed on the packaging side, but the python community / existing libs (the entire rest of the ecosystem) is miles ahead of Go's at this point.
Bold choice.
I hear people praise the Java ecosystem so often, but when I use it I cannot replicate that.

There is nothing like Django in Java land? Instead you are sent into a confusing mess of interface standards like JPA and implementations (hibernate, ebeans, ...) and little guidance how to choose.

Equivalent of Django would be something like Spring Boot, Play Framework etc.

It's quite common that some of these frameworks rely on community infrastructure that has several competing implementations, like JPA, but the point of a tool like Boot is that they picked all the implementations for you and documented it all centrally.

We tried Play. It is not close to Django. At least with Java. It might be better if you use Scala.

For example, one thing which seems to be worse across the board is database evolutions. Django will autogenerate an evolution in Python for you [0] and if necessary you can adapt the code. In Java land you have to write SQL by hand [1] afaik? If SQL is not enough, I don't know what to do at all.

[0] https://docs.djangoproject.com/en/1.11/topics/migrations/ [1] https://www.playframework.com/documentation/2.6.x/Evolutions...

Hibernate can do automatic migrations, but I think most complex deployments prefer to take direct control of the process.
Fair enough. I should have said one of the best. A bit of sensualism there.
Really love your language ;)

Sensationalism maybe?

Yes, of course, thanks for correcting me!

WTF is wrong with me?? I should be banned from this forum for life!

> If Go had ADTs

In the process of hacking on my PureScript2Golang "transcompiler", I've realized that under the hood ADTs, also known as "tagged unions", will likely more often than not be represented exactly as just that: a "tag flag" and a data pointer. (Of course if all of an ADT's ctors are nullary, an enumish int will do the trick just as well --- I doubt this gains anything over interface{}-boxed 0-size struct type-defs though in reality.)

Now the natural "box"/container for this tag+content pattern in Go is the `interface{}` that can hold as 'content' any of your "constructors" (whether it be a struct or type-alias or prim type), plus your 'tag' pattern-matchings translate to Go's type-assertions. You see this also whenever ADTs are translated to JS or OOP languages, in some manner the "type" tag is carried along to be able to switch-case on.

Anyway, you can do poor-man's ADTs as per above today. The catch is that you get fewer compile-time checks (won't check for pattern exhaustiveness in your switch-cases, or illegal/impossible/invalid (as per your custom "dumb" ADT layouts) type assertions).

But anyone who's done some linked lists or devised parser ASTs in Go has written "ADTs" in this manner --- whether by intention or inadvertently =)

Personally, I don't care about Generics now that I'm aiming to generate low-level Go code for PureScript's parametric polymorphism and higher-kinded types system.. --- expressive beyond the old-school oop/imperative "generics" band-aid ;)

> That I can add interfaces implementations to structs I don’t own

Wait, did I miss out on some new feature only recently introduced? The way I parse your above statement, it sounds like you could define methods for receiver types imported from other packages, is that what you meant? Or did you simply have classical workarounds in mind, such as type-aliasing or set-the-"method"-as-a-struct's-func-typed-field?

Can you link to your project?
Current approach: http://github.com/metaleap/gonad-corefn (to be renamed later) --- heavily in-flux, even more slow-going pretty-soon-now (long-term freelance gig coming up), but I'm committed to keep committing =) --- won't be polished in terms of docs/tests/better-commit-msgs etc until reaching a certain stage of completeness. Also about 1/3rd of the code can be ditched at some point, coming from earlier stages --- only will become fully apparent which 1/3 at a later stage of maturity as well though.

Earlier first approach, won't currently compile due to deps: http://github.com/metaleap/gonad-coreimp

Auxiliary stuff currently dumped in: http://github.com/golamb

For a fun ps2go comparison, side-by-side:

https://github.com/golamb/test-pscorefn-src/tree/master/src/... <-> https://github.com/golamb/test-pscorefn2go/tree/master/Mini --- lots to-do still =)

What's the connection between `defer` and goroutines?
Not a strong connection, but both features are managed by the Go runtime.
Yeah, none. I wrote this while still in bed. No idea what I was thinking at the time :D
None, the feature that completely depends on defer is recovering from panics.

The order in which defer statements are executed is predictable, unlike goroutines.

Novice here. Why is having no exceptions a good thing, in your opinion?
In Go’s philosophy most problems are not exceptional. File can’t be opened? This is to be expected on systems with file permissions. The common pattern is for functions to return an error type as last return argument (Go supports returning multiple arguments). Programmers can then handle the problem right then and there if the error is not nil. This approach also helps to keep control flow straight forward. The happy path flows from top to bottom. If an error occurs, it is typically handled in an if-block. There are no nested exception blocks or exception blocks that follow each other and do things far removed from the exception’s origin in the code. As a result Go code looks very clean and is easy to follow.
It's actually pretty common for a file exception to be exceptional.

In all programs I've ever worked on you don't want to have to check useless junk like that and just want the general exception handler to handle it.

This isn't true at all depending on your work. I program software that has to work alongside other programs on busy file systems...failing to read/write a file is not someone else's problem...it needs to be handled, logged, there is potentially a retry and/or notification.
For exceptional, unforeseen situations you do have exceptions, aka "panic".

For signaling error conditions that the caller has to expect and handle, you have the `result, err = func(...)` idiom, and a compiler that would warn you if you forget to use the value of `err`.

If Rob Pike's opinion on this is not enough, here's Martin Fowler saying essentially the same thing: https://martinfowler.com/articles/replaceThrowWithNotificati...

In general: http://wiki.c2.com/?DontUseExceptionsForFlowControl

Go does not warn you if you forget to handle an error. It only does so if the function in question also returned a value that you're using. There exist important functions that don't return non-error values and report errors that you very much would like to avoid dropping on the floor: os.Chdir() for example.
While this is technically true, I've considered `errcheck` to be standard tooling for what feels like forever now, and it does exactly this; make sure you're checking your errors.
I’ve read the first article. It was eye opening, cause I am writing a bot right now and using exceptions to control input flow. Made me think about my design.

That said, it does not argue against exceptions. So, I am not sure what your argument is.

Exceptions are bad outside the "your computer just started burning" cases, but Go has replaced them with something even worse, "multiple return values".

So instead of some imagined return of "int or throw Exception" you now have "(int, error)", which basically means that the result of a function call can be any of these four options:

  - (   value, no error)
  - (no value,    error)
  - (   value,    error)
  - (no value, no error)
And due to the lack of Generics you can't abstract over your error handling.

And due to the lack of proper ADTs you can't even properly model "value OR error" manually.

The last case is rarely seen in Go (at least not in the standard library).

Accepting for the moment that Go has no exceptions and and error returns are the way to go, the first two cases make sense.

The third case ( value, error) is actually useful in several scenarios. For instance, considering you're writing bytes to a stream that fail partway. The value is the number of bytes return so far and the error is the error that was encountered. In fact, this is the signature that the ubiquitous io.Writer's Write method uses.

If all you had was single return values (aka "int or throw Exception"), how would you model the io.Writer?

> If all you had was single return values (aka "int or throw Exception"), how would you model the io.Writer?

This is not an problem, because a single return value is _not_ everything one has.

The last two options are not idiomatic Go. (zero, err) and (nonZero, nil) are. So the error check is almost always a simple `if err != nil {return err}`.
> The last two options are not idiomatic Go.

Who are the authors of https://golang.org/pkg/io again?

Yeah, 90% of Go devs agree with you.

10% think it is a really great idea.

So you can never be sure without reading the documentation.

Feels like Javascript callback error handling. Which was not much fun.
Just without the callbacks. At least you could abstract over error handling with callbacks...
Because it makes you handle errors by value, which makes you handle them one by one, and not group everything into one try statement.

This usually makes the error handling much better and better thought through.

You can see the same thing happening in all the functional languages using the Either monad instead of exceptions.

Exceptions are one of those things which are really nifty when programming in the small. However, they can create outsize problems when programming in the large.
>Actually (5) is one of the few things I don’t like about Haskell.

Haskell has exceptions FYI

https://wiki.haskell.org/Exception

Yes, that's what I say I don't like. Sorry for not making myself clear.
Lack of exceptions is the main reason I won't use it.
I thought this as for a long time. Then I used go for awhile and I think I like the go way better. It solves the issues I had with execptionless languages without all the noise.

With exceptions you most often just let them trickle up the call chain, which is the same thing you often do with err, just return it.

And the returning the err works much better when doing async. Cross thread exceptions are a PITA and you are basically back to just returning an error obj.

On the whole I agree that I prefer Go's explicit error returns over other languages implicit "alt-return paradigm" aka exceptions. But you're really well advised here to run a couple of linters occasionally that inform about ignored/unchecked/non-passed-on errors, it can happen all too easily while "rodeo-coding" prototypes/MVPs
I thought this as for a long time.

I have tried Go and worked with languages without exceptions in the past. The problem is the amount of error handling code I was writing time and time again. It might only be a case of returning an error but that is one more thing you need to think about instead of concentrating on the the domain.

Error handling in threads is a pain either way.

Lack of exceptions (in my code, but more importantly in library code) is one of the main reasons I use go.
Defer is not enabled by goroutines. C++ has `defer` as well, although it's called "destructors" there.
Heh, that is fun: the thing I hate about go is immature very varied stdlib quality, leading to even more varied ecosystem.

Rust seems on the right path, go ... well do you even have package management after 8 years? No..

What other systems do you compare it with? Its not the best I guarantee it...

I wonder how the presence of ADTs and absence of generic functions would mesh together.

With ADTs, using a sum type instead of the `result, err = func(...)`, would be an obvious thing to do. But the next thing you'd consider would be `bind` / `>>=`.

I'm not sure why the next thing you'd consider is bind. It seems like plenty of languages with ADTs and generics still either don't have it available in their libraries, or use it extremely lightly, in spite of being able to implement it.
I'm referring specifically to the error-handling situation.

If you replace the two-value assignment with an `Either`, it feels very natural to also eschew the constant `if (err != nil) return err` lines, and adopt a continuation-passing style, the way you do with Futures in JavaScript. You could even consider some syntactic sugar around it, like the `?` operator of C# and Kotlin, or a full-on Haskell-like do-notation.

Again, it doesn't seem like this is something that many communities do, in spite of having languages that support it. The '?' operators in the languages you cited, as far as I recall, are specific to null, and don't work on generic option types.
Go has (inferior) exceptions, they are called panics. error as value is not a feature of the language, it's a convention.

> 1. Probably the best ecosystem out there.

it will when it gets an acceptable PDF library, better enterprise integration (SOAP and co) and an actual debugger (and no Delve isn't good enough given how it often fails at basic debugging).

#5 is not a positive thing.
Depends IMHO. I love exceptions in Python, find them a bit ugly in Java and hate them in Haskell.