1. 32
  1. 8

    I was initially worried that they dropped the “we will never break your old programs” promise but that is not the case.

    https://github.com/golang/go/issues/40025

    Obviously we cannot delete these three from io/ioutil. But we can move the implementations to io and leave wrappers behind. That will be easier for new users, and it lets more packages do general I/O without a dependency on os.

    1. 1

      Though I consider it to be a bit silly, it seems they plan to retain the package in a deprecated state (for forever?).

      1. 1

        I believe they will eventually remove it from newer versions as adoption gets wider spread.

        1. 12

          A future hypothetical “Go 2” might; the Go 1 compatibility promise is pretty clear that any program written in any version of Go 1 should always work with newer Go 1 versions (with a few exceptions that don’t apply here).

          As I mentioned the other day, there are already a bunch of deprecated/frozen packages that aren’t removed for exactly this reason.

          1. 3

            Yeah, this is the problem with standard libraries. They have to stay on “semver v1” forever. You better get everything right on the first try, because everything has to be maintained forever.

            I wish languages treated their standard library like every other package. 3rd party packages can exist in multiple versions, so semver-major changes are only a migration, and don’t have to break anyone.

            1. 10

              I wish languages treated their standard library like every other package. 3rd party packages can exist in multiple versions, so semver-major changes are only a migration, and don’t have to break anyone.

              I am so glad Go doesn’t work this way. It means there aren’t “eras” of codebases that utilize totally different subsets of language features.

              1. 10

                Go absolutely has that. One prominent example is code written before the context package and code written after it.

                And tooling wise, there are projects before Go modules and projects after it.

                1. 3

                  That’s missing the point. Code written before the context package will work when copied and pasted, patched, incrementally migrated, or otherwise mingled into code written after the context package.

                  1. 7

                    That’s a good difference to point out, sure. But I don’t think it means my comment has missed the point. There are still costs associated with shifting subsets of the language/std beyond the ability to copy & paste. Look at net/rpc. It is at best unwise and at worst unusable in the code that uses and relies on context cancellation. Because it is a package written in a different era of Go.

                  2. 1

                    Technically, yes; effectively, I don’t think so. Changes to the stdlib and tooling are infrequent, and almost always come as additions, rarely depreciations, and almost never iterations on existing idioms. Go code will still bit rot over time but I think it is categorically different than the e.g. Rust ecosystem.

                    1. 2

                      Rust code bitrots faster than Go code. Absolutely yes. But I don’t think there is a category difference. It’s a difference of degree. That’s a feature of Go that I appreciate, as the churn in the Rust ecosystem annoys me. And IMO, there is not nearly as much appreciation for the annoyance of churn in Rust’s culture as there is in Go’s culture. I would say the difference in philosophy here is probably a category change.

                      But my point is that Go has eras too. Best not to thumb your nose at the idea. Maybe thumb your nose at the frequency or scale of changes instead. The point is that churn/eras still happens in the Go ecosystem. I’ve lived through it since Go 1.0.

                  3. 3

                    Just because you don’t name the “eras”, it doesn’t mean they don’t exist. You still have deprecated features. There will be “still using ioutil” codebases, and “moved off ioutil” codebases (and such implicit “era” for every other deprecated feature). There are always some laggards — that’s the reason why the no-breaking-changes promise exists.

                  4. 4

                    This isn’t a problem, it’s a benefit. I don’t want to have to rewrite my code to the flavor of the day if I want to use a new feature. Compatibility breaks in widely used libraries should come with a heavy burden of necessity.

                    Even outside of the standard library, an attempt at compatible evolution is a big benefit.

                    1. 1

                      The reason that isn’t done is because the standard library contains a lot of glue, just like the language itself does. Major “non-standard” libraries with data structures that get used as API parts don’t want to make breaking changes for the same reason Python 3 was so painful. Everybody has to upgrade at once.

                      This isn’t as much of a problem for Go interfaces, which are structurally typed, but it’s a serious problem for struct, which is nominally-typed. Plenty of Go functions, even in the standard library, accept struct parameters.

                    2. 2

                      Go (and Rust) seems to be rather amateurish in that regard.

                      Having no actual removal policy is only one step above an “ignore compat” free-for-all.

                      1. 7

                        Wait, ‘maintain guaranteed compatibility’ is only one step above ‘ignore compatibility’? Sounds backwards to me.

                        1. 0

                          You think ‘ignore compatibility’ should be above ‘maintain guaranteed compatibility’?

                          (I don’t really care, I think both are playing at roughly the same amateur level, i. e. we should do much better in the 21st century than that.)

                          1. 2

                            What do you propose? Which ecosystems out there do this right, in your opinion?

                2. 3

                  I find it a little frustrating that they renamed the functions too.

                  In the given example, if they had moved the function TempDir into the os module, the only change that would be needed would be deleting the io/ioutil import. But they also renamed the function so the code needs to change as well.

                  1. 9

                    os.TempDir() already exists and is used to get the system’s temp directory (i.e. ${TMPDIR:-/tmp} on most Unix systems).

                    ioutil.TempDir() will keep working; you don’t need to update any of your code, and I believe go fix should make all the required changes anyway.

                  2. 1

                    I always did wonder why ioutil even existed. Maybe strconv can go next?

                    1. 1

                      ioutil was a hack to avoid cyclic dependencies: os refers things like io.Writer, io.Copy, etc. and ioutil.ReadFile() refers to os.Open(). By just moving those functions to os that problem was solved.

                      I’m not sure about strconv; I suppose it could be in strings. I looked a bit at the git log and functions like Itoa() used to be in strings. I can’t really find more information on this with some Google-fu, those CLs don’t seem to exist in current Gerrit, and there is no discussion on go-nuts about it either. I wouldn’t be surprised if it was somehow a similar problem although the packages don’t reference each other 🤔