News for hackers. Stuff that matters.
Re:Go 1.18 by fpoling on Tuesday March 15, 2022 @02:33PM (#30689114)
For me the most puzzling aspect of Go is channels. Ada tried CSP (Concurrent Sequential Processes that Go channels are based on) in eighties and people quickly realized that it lead to bad performance and was unsuitable for a lot of useful cases. So Ada got standard mutexes and signals.

So why CSP which does not allow to implement priority delivery or multicasting and makes cancelling much harder compared with normal polymorphic message queue per thread? Moreover, Go crippled CSP even further as one cannot select on channel and file descriptor.

Fortunately Go does provide mutexes and signals so one can ignore channels unless required by Go API. Still without proper sum types polymorphic and type-safe message queues are not possible.

Re:Go 1.18 by coder543 on Tuesday March 15, 2022 @03:35PM (#30690297)
> Still without proper sum types polymorphic and type-safe message queues are not possible.

This isn't really true.

You can define a channel where the message type is an interface, and then you can use a type switch on the receiving side to downcast that interface to various concrete message types.

What you can't do without sum types is ensure that you don't need a "catch-all" branch for if/when a value is sent down the channel that isn't one of the concrete message types you expect, but it will still be type safe and polymorphic.

Re:Go 1.18 by bsaul on Tuesday March 15, 2022 @07:45PM (#30693451)
That’s a weird definition of type safe… with very very low standards.

What OP probably means is that there’s no way to build a channel transmitting a list of various types while having the compiler warn you if you’ve forgotten a possible type, or if you’re trying to unwrap into an impossible one.

Re:Go 1.18 by coder543 on Tuesday March 15, 2022 @07:54PM (#30693521)
> That’s a weird definition of type safe… with very very low standards.

Thanks.

> What OP probably means is [...]

I already completely addressed this point in my previous comment. Your comment doesn't appear to add anything new to this discussion. What OP actually sounded like they were saying is that you would have to do an ad-hoc union type with a struct containing the fields of the various things you would want to send, and having to hope you don't access the wrong fields at the wrong time. That would be type unsafe.

I've written a lot of Rust professionally over the years, among other languages. I'm very familiar with what strong type systems look like. Being told my standards are "very very low"... such a great way to keep this conversation constructive. Type safety is a spectrum, and what I described in my comment above is far from "very very low standards" when talking about practical applications. It's not describing the gold standard -- I'll be the first to say that I wish Go had proper sum types -- but some people apparently forget what a lot of other popular languages deal with in terms of type safety, leading to hyperbolic statements about other people's standards.

Re:Go 1.18 by fpoling on Wednesday March 16, 2022 @02:36AM (#30695951)
By type-safety I indeed also implied inability to send messages that the receiver would not process. This is essential to allow safe refactoring.
Re:Go 1.18 by randomswede on Wednesday March 16, 2022 @05:08AM (#30696719)
What I have done in the past when I've needed to pass various different types across a channel is to look at what the receiver of these messages actually need to do, factor that out into an interface, make a "chan myInterface", then make sure that every data type I need to pass across actually implements that interface.

The main thing I am working on (slowly, because time and stuffs) is a client library for an saync-protocol messaging/forum platform, where each request will (eventually) receive a response, so I send the request, then construct a request-specific data type to pass across to the listener, together with the ID of the request, so that when the response returns, it can dispatch to the proper decoder for the response.

That actually seems easier to describe in code, than in words, but this margin is too short for that.

Re:Go 1.18 by zozbot234 on Tuesday March 15, 2022 @02:38PM (#30689178)
Go has shared memory and mutexes, but using them means giving up on memory safety because the whole rigmarole that Rust does to preserve safety in concurrent code is totally missing in Go. So that's a meaningful reason to stick to CSP - sure, it might create easy deadlocks but at least it won't totally crash your app with a potentially exploitable fault.
Re:Go 1.18 by fpoling on Tuesday March 15, 2022 @02:49PM (#30689341)
Go could have provided message queues with priorities Erlang style or something closer native solutions in Linux/Max/Windows that are known to work. They are safe, allows to avoid deadlocks just as channels and much more flexible.

And with mutex one can build those as necessary, even if interaction with channels is ugly.

Re:Go 1.18 by sriram_malhar on Wednesday March 16, 2022 @12:07AM (#30695211)
Erlang doesn't have priorities natively, does it? As far as I am aware, there is a standard pattern to do it, like this:

    receive
        {high_p, Message} ->
            process_high(Message)
    after 0 ->
            receive
                {high_p, Message} ->
                    process_high(Message);
                {low_p, Message} ->
                    process_low(Message)
            end
    end 
The Go equivalent can be done with different channels for priorities and similarly nested selects, as I am sure you are aware.
Re:Go 1.18 by ergl on Wednesday March 16, 2022 @10:32AM (#30699304)
Erlang has selective receive, meaning it will go through all your receive branches in order, until one matches.

With this code:

    receive
        {high_p, Message} ->
            process_high(Message);
        {low_p, Message} ->
            process_low(Message)
    end
if the process has one high_p message and a low_p message, the high_p message will be processed first.
Re:Go 1.18 by fpoling on Wednesday March 16, 2022 @02:29AM (#30695916)
Hm, is this pattern the best Erlang has and VM does not optimize it to avoid the need to lock the queue twice? If so then you are right double-select is equivalent even if it is rather messier in Go then in Erlang with the need to provide 2 channels.
Re:Go 1.18 by dragonwriter on Wednesday March 16, 2022 @12:14AM (#30695247)
> Go could have provided message queues with priorities Erlang style

Erlang doesn't have priorities; you can fake them with selective receive, but IIRC selective receive is considered a code smell and something that should generally be avoided or minimized.

(You can also have a separate process act as a priority queue channel by, well, storing messages in an actual priority queue structure and sending from its head, and that's a little more robust.)

Re:Go 1.18 by avl999 on Tuesday March 15, 2022 @04:55PM (#30691583)
Avoid channels except for straight forward, producer-consumer workflows. Use mutexes and other traditional concurrency primitives for other stuff. Channels are a huge shotgun with caveats.
Re:Go 1.18 by gilgad13 on Tuesday March 15, 2022 @05:49PM (#30692257)
I agree, with the addition that closing an unbuffered `<-chan struct{}` is a good way to do broadcast notifications (a. la. the context package).

As evidence that channels should be used rarely, and only in small scopes, consider the number of places chan is used in the standard library.

Re:Go 1.18 by lanstin on Wednesday March 16, 2022 @01:19AM (#30695582)
The thing about channels is, while they might be slow for tight inner loop type compute code, it lets you scale across all the cores very linearly. And, given a channel to flow the work thru, you can manage the number of workers at each stage however you like - growing with more load, dropping work with more load, back pressure with more load. All concisely and explicitly. And a lot of network server things are classic producer/consumer things, so it’s great to have a solution for that where you get concurrency without any thread worries.
Re:Go 1.18 by bashery on Wednesday March 16, 2022 @09:04AM (#30698238)
+1
Re:Go 1.18 by pkulak on Tuesday March 15, 2022 @02:35PM (#30689138)
I can never seem to understand why a GCed language has pointers, and makes you memorize when to use stack vs heap.
Re:Go 1.18 by Laremere on Tuesday March 15, 2022 @02:55PM (#30689427)
Go uses escape analysis to ensure memory safety of pointers. Taking a reference of a local variable means it might end up on the heap, and calling new might allocate on the stack. It all depends on if Go can determine that the lifetime of the pointers is less than the stack frame.

It's good if you don't want to care about those details, it's bad if you really care about those details for performance reasons.

Re:Go 1.18 by mftb on Tuesday March 15, 2022 @02:45PM (#30689275)
It's pretty straightforward, knowing when to use the stack vs heap can improve your performance. Having both paradigms available in the language means you can start out relying on the GC for everything. Later when you're ready, you can look to make more intelligent use of the stack.
Re:Go 1.18 by bashery on Wednesday March 16, 2022 @09:07AM (#30698283)
+1
Re:Go 1.18 by lawl on Tuesday March 15, 2022 @07:29PM (#30693325)
Hits the sweet spot for me between the one extreme of having to malloc/free everything myself and the other extreme where everything is immutable, garbage collected and you dont know what causes allocation.

The former is a nightmare for security and productivity, the latter is a nightmare for performance.

Re:Go 1.18 by int_19h on Tuesday March 15, 2022 @10:16PM (#30694519)
Go can implicitly heap-allocate even a local variable if you e.g. capture it in a closure. This is actually more implicit than you get in Java or C#.
Re:Go 1.18 by morelisp on Wednesday March 16, 2022 @03:08AM (#30696087)
Java can decide to heap-allocate or not heap-allocate at runtime based on what optimizations the JIT has performed so far, and so it can change over the course of a program's run. I don't think you can get more implicit than that.
Re:Go 1.18 by int_19h on Wednesday March 16, 2022 @06:18AM (#30697079)
Why would Java ever heap-allocate a local, regardless of optimizations?

It can decide to heap-allocate or stack-allocate objects (when you new them) based on escape analysis, which is a JIT optimization. But that's very different from heap-allocating variables.

Re:Go 1.18 by morelisp on Wednesday March 16, 2022 @07:02AM (#30697355)
Then I'm not even sure what you're talking about when you say "heap-allocate even a local variable" if you don't actually mean the value of that variable. If you draw that distinction, "variables" are never allocated; spaces for values are.

Regardless, whether something is a local or not in Java still varies depending on the JIT's decisions. (Or rather, the JIT does not see local/non-local variables, only lifetimes, and whether the lifetime may extend past the JIT's current view.)

Re:Go 1.18 by int_19h on Wednesday March 16, 2022 @07:52PM (#30705700)
In Java, the value of the variable of a class type is not an object - it's a reference to the object. Those references are always stack-allocated. Variables of primitive types like ints are also, of course, stack allocated.

In Go, if you have a local of type int, and that local is captured by a closure, it will be allocated on the heap to extend its lifetime. Java doesn't need to do this because it requires captures to be effectively final. On the other hand, C# does the same thing as Go.

Re:Go 1.18 by foldr on Tuesday March 15, 2022 @05:34PM (#30692085)
>and makes you memorize when to use stack vs heap

You don't have to think about stack vs. heap allocation in Go unless you're writing performance sensitive code. You could pretend that Go heap allocates everything and still read and write Go code without any problems.

Re:Go 1.18 by hnov on Tuesday March 15, 2022 @02:52PM (#30689380)
I think it's at least partially about value vs (mutable) reference semantics in Golang's case and the hope is that the compiler will know to whether heap or stack allocate based on escape analysis.
Re:Go 1.18 by pjmlp on Wednesday March 16, 2022 @04:28AM (#30696515)
Performance, Java made the mistake to not have good ways to deal with GC free allocations and now who knows when Valhala will ever happen.

Thankfully plenty of other GC based languages hadn't made the same mistake.

Re:Go 1.18 by Zalastax on Tuesday March 15, 2022 @06:25PM (#30692741)
You may find my MsC thesis, link in profile, interesting! If I remember my writing correctly, I have similar reflections on CSP.
Re:Go 1.18 by chombier on Wednesday March 16, 2022 @05:15AM (#30696757)
Maybe the addition of generics could enable Concurrent ML-style composable channels?

This wouldn't solve the performance issue, but at least it would provide a nice abstraction for protocols.