Hacker News new | past | comments | ask | show | jobs | submit login
Go 1.9 Beta 1 release notes (golang.org)
172 points by bradfitz on June 14, 2017 | hide | past | favorite | 54 comments



I hadn't looked at the planned changes for go1.9 until now and I have to say I'm really happy :)

- net.Resolver.Dial allows doing all DNS resolution myself, right in the same process (via a localhost port). And nice it can do both UDP and TCP.

- SyscallConn providing access to the underlying file descriptor is super useful too.

- monotonic time is very useful in network protocols.

- concurrent.Map is nice too.


Java has had concurrentMap forever


So did Python. What's your point?


Chuffed about math.bits. You can always implement these yourself, these are short (albeit a bit cryptic) snippets, but having these in the standard library means that they will be regularly updated for various architectures and CPUs.

Good stuff.


> You can always implement these yourself

Check the discussion, as usual things aren't as simple as they seem: https://github.com/golang/go/issues/18616


Will need to double check some machines to make sure these two don't bite me:

On Unix systems the environment variables SSL_CERT_FILE and SSL_CERT_DIR can now be used to override the system default locations for the SSL certificate file and SSL certificate files directory, respectively.

The os/exec package now prevents child processes from being created with any duplicate environment variables. If Cmd.Env contains duplicate environment keys, only the last value in the slice for each duplicate key is used.


I would be super interested if the os/exec change bites you. I've never seen any definition of how programs should handle duplicate environment variables, and there doesn't seem to be consistency in the wild either.

We went with the bash behavior (latest one wins), which also means all the code previously using:

cmd.Env = append(os.Environ(), "KEY=newvalue")

... now actually works reliably. Previously, if KEY=oldvalue was already defined, what you got depended on your luck with the child program. Either old, new, or random.

File a bug if you see a regression.


I wonder if that could cause security issues with setuid binaries. But then, if you make a Go program setuid and it executes other programs, I guess that’s already asking for trouble. I was thinking it might be possible to use duplicate environment variables to confuse the dynamic linker’s dangerous-environment-variable filter, but that filter would only run in the first place if the Go program happened to use dynamic linking; otherwise there’d be no protection at all (unless Go has some built-in knowledge of what variables the dynamic linker cares about). So just don’t do that, I guess…


Yea, I'm thinking about cases similar to HTTP parameter pollution, and what the program expectations are. You'd be right to argue environment variables should not be user controlled. :)


Note that only the os/exec package has this new cleanup logic.

If you really want to do something nasty and intentionally confuse your child processes, the lower-level os.StartProcess and syscall packages are there for you. :)


> On Unix systems the environment variables SSL_CERT_FILE and SSL_CERT_DIR can now be used to override the system default locations for the SSL certificate file and SSL certificate files directory, respectively.

This will fix so much pain that my team is having right now. I am glad that it finally is available.


Type aliasing!

  type T1 = T2
I'm currently writing a pretty large project in Haskell and am thoroughly enjoying it -- as everyone and their mother says, the type system is superb, and even simple aliases like:

  type UserID = Int
Make it so much easier to read and understand functions.


I'm not sure if type aliasing is for that use case. I think you're more thinking about things like

  type Celsius int
that in theory prevent you from passing a bare int and mixing up Celsius/Fahrenheit.

In go this almost works. If you define

  type Celsius int

  func isBoiling(t Celsius) bool {
      return t >= 100
  }
then You need to call it by doing something like

  t := Celsius(100)
  isBoiling(t)
and just

  t := 212
  isBoiling(t)
will not compile with

  cannot use t (type int) as type Celsius in argument to isBoiling

But unfortunately you can get away with

  isBoiling(212)

However, If you just used a type alias then you'd be able to do either.


Right. You could always define a new type with an existing underlying type, like Celsius with the underlying type int in your example. See the spec here: https://golang.org/ref/spec#Types

This would define a new, non-interchangeable type, even though it's just an int behind the scenes. The new type alias on the other hand define a new name for an existing type; you still have just one type. In the user ID example above that's not what you want, it would allow using an int where a UserID is required.


As I understand it, there have been a couple of compiler-blessed aliases for a long time (byte -> uint8 and rune -> int32), making this a similar thing but now at user level.


> But unfortunately ...

It is not due to some unkind turn of fortune that the Go type system drops the ball here and there.

> However, If you just used a type alias then you'd be able to do either.

The important distinction between a type and an alias is that you can define methods for a type and you can not do the same for an alias.


Thanks for the clarification! You're absolutely right -- Go's regular types would have actually helped with that.

I don't know that I would build the same kind of type machinery though (I might only use the alias, not the actual type), since a call like

  isBoiling(212)
would fall apart.

In particular, I think the type aliasing will make function signatures a lot clearer -- if I to write a `Celsius` type class, I would likely just make a small, hopefully simple `Temperature` type. Unfortunately last time I wrote any Go code, I wasn't anywhere near that careful/purposeful with the types (I mostly left things as `int`s, etc)


Go constants are untyped, which means you can pass a number as argument to a function that takes a type whose underlying type is an integer (e.g. time.Duration) without having to explicitly mention the type. That's how time.Sleep(5 * time.Minute) works


> But unfortunately ...

You can't get away with:

    isBoiling(Fahrenheight(212))

    cannot use 212 (type Fahrenheight) as type Celsius in argument to isBoiling


You can if

    func Fahrenheight(f int) Celsius {
      return Celsius(5*(f-32)/9)
    }


Well, sure, but I meant that in the context of:

    type (
        Celsius    float64
        Fahrenheit float64
    )


You should Newtype it though. Type aliases aren't substitute for semantics. They are for to simplify naming of complicated types.


In the Go world, they also serve as a way to gradually shift code around. Large code bases tend to have parts which are on transition from an "old" way to a "new" way, and type aliases allows you to maintain a backwards compatible stub type for a newly changed system.

This then avoids the patch of 40k lines changed as you can gradually go to different teams and get a checkmark that the changes you made to the teams code is still going to work as expected. This is usually better coordination-wise than a 40k line patch everyone has to sign off on.


> ...the environment variables SSL_CERT_FILE and SSL_CERT_DIR can now be used to override...

I presume that those environment variables override the defaults at runtime, but it would be good to call that out explicitly since we are talking about a compiler release and there are compilers that can evaluate environment variables at compile time to allow for more flexible constant runtime values.


It would be really nice if each change in the release notes linked to the GitHub issue and/or PR, so curious folks can see how each fix or improvement is implemented. For example, see the notes like "Contributed by Victor Stinner in bpo-26146" in the Python What's New documents: https://docs.python.org/3/whatsnew/3.6.html


Interestingly, the CL (changelist?) numbers are in the HTML as comments, so if you do View Source you can see them. And then you can go to (for example) https://golang.org/cl/42028 to see the associated code changes. Not sure why these aren't user-visible links.


Is there anything being discussed about Go 2.x?

I haven't seen anything about it, which I absolutely love (based on fear of a Python 3 situation).


We on the Go team team also muse about a Go 2.x occasionally (no concrete plans), but the number one rule would be not pulling a Python 3 or Perl 6 and fracturing the community in two.

If/when it happens, it won't be scary.


Maybe you guys could do like Java did and eventually drop the 1.x from the naming, leaving just the x, or do you see any issue with the perception of backwards compatibility?


I think that languages like Go are objectively speaking in much better position to release 2.0 or 3.0 updates because we have the example of Python 3 and have some pointers what not to do.

I think even major (=with source-level breaking changes) version bumps are feasible specifically with compiled languages. This is because you can still continue to support the both versions of the language in the compiler, and if you are careful with semantic changes, you can support linking packages of two different versions together.

Additionally, if you start designing breaking changes from the start with the mentality that an automatic tool should be able to upgrade the code base, that eases up the migration a lot. (There is 2to3 with Python, but that didn't always work, which to me means that the language changes weren't exiplicitly designed to be automatically upgradable.)

Btw. I wrote this thinking that a major version bump means source-level incompatibilities – but it's possible, although challenging, to introduce major new features even without.


In addition, a static typed language allows you to find most breaking changes at compile time. The bytes/str situation would easy to fix in golang, instead of exploding at runtime now and then as in Python3.


Go 2.x is not much more than a collection of thoughts, improvements, and opportunities that could come about with breaking backwards compatibility. No real work, to my knowledge, is even planned for it.


FYI, The next release is going to be 1.10


Cool, DWARF improvements are definitely welcome. Even if these ones are just for Windows. ;)

Go's debugging story, for things using cgo, has been a bit of a rude shock (to me). As of yesterday's toolchain updates (Gogland, Delve), single stepping on Linux is now working reliably (eg not randomly hanging).

It still needs to be improved so instructions execute in the order they're written (eg more disabling of compiler optimisations when debugging), but that's less important than fixing the "ugh, it's just hung again!" bug was.

Hopefully OSX debugging gets to a similar (working ok) state soon too. :)


When should one use sync.Map vs a plain old map with a RWMutex?


Probably the same with any other locked data structure vs. structure using atomic ops. If you have read-heavy workload, especially one that does batching then grabbing a read-lock once and executing your batches before releasing it can be faster than lots of reads on a current map. Especially on weakly ordered architectures where the ordered reads may cost you performance.


That makes sense. I was hoping someone would have some concrete benchmarks for different access patterns but I don't see any in the github issue[1].

[1]: https://github.com/golang/go/issues/18177


When one does not care about type safety ;)

Only sorta kidding... Personally, I think sync.Map is a dangerous addition to the standard library, and hope that the Go team does not encourage using the empty interface{}'s like this. I can only imagine how bad it'll feel when someone reads an int64 as an int.


Its not really any different context.WithValue where the strongly encouraged idiom is to have all access go through typed get/set methods that handle the type casting for you.


Although I agree with you about encouraging interface{} (there is even a "proverb" against its usage), you cannot read an empty interface as a type it is not.

Unlike C void-pointer, interface{} is the tuple (type, value) and you need a type assertion for the right tipe to get the value.


Until they introduce some kind of type safe genericity language level support, interface{} and go generate are the only options available for generic data structures.

Personally I would already be happy with what CLU allowed for in 1975, no need for anything more expressive.


There is a third option which is to add specific data structures to the existing list of generic data structures with out making user defined generics.


Which leads to the question when would they stop adding special case structures?


(shrug) they clearly are ok with some generics. In my mind if they argued that they'd rather support more widely used data structures at the language level instead of user defined generics thats a logically consistent position.

In fact, if I could get a) a type safe concurrent map b) a promise/future construct and c) a fast concurrent queue for single writer multi-reader, I'd probably be ok with the set of generic data structures provided in the language.

If they fixed their variance rules along the way as well, I'd probably lose any actual arguments against the type system when it comes to the systems I build.

The question is of course, are those the only ones that fall into that category? Who knows but those are the ones I run into the most pain for.


I'd go further and say when one cares less about performance. There's overhead for type assertions that could be avoided by implementing your own synchronized map with the types you desire. Admittedly, the overhead is probably negligible for most users.


generally don't want to use RWMutex unless you can help it; the performance is much worse than the regular sync.Mutex


Seems like the monotonic time changes mean that some calls (e.g. time.Now()) are making two syscalls where they used to only make one? And storing two different clock values? Could make code that makes a lot of timing calls more expensive/slow...


The size of the time.Time data structure is unchanged on 64-bit. There was space to pack it in.

As for time, on Linux it got 70 ns slower. Tolerable. (It went from 70 ns to 140 ns on my GCE VM)

On Windows, it actually got faster on 64-bit with the addition of:

https://github.com/golang/go/commit/e4371fb179ad69cbd057f243...

And on macOS, where nobody runs servers, it's basically unchanged speed-wise.


Cool. I've just been instrumenting some fairly hot Go code with time.Now/Since all over the place trying to pinpoint some lost milliseconds ... I was planning to keep the instrumentation in there for monitoring... Sounds like it shouldn't be a problem.


On Linux there are no syscalls for time as the time is deduced from values in a read-only page that is provided by kernel.


still no generics :(


What does sync.Mutex is now more fair really mean?


It probably means that it used to favour activating one waiter over others, but now is less likely to do so.


Imagine Alice acquires the lock.

Before she releases it, both Bob and Cathy ask for it.

So it goes to Cathy when it's released.

Before Cathy releases it, David requests it.

If the lock goes to David (and then possibly Emma, Fred, and Gary, and so on forever) before Bob finally gets to continue executing, then the lock is not fair.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: