5 ms·
> That's what the New function does, isn't it? But that's only run when the pool needs to allocate more space. What GP seems to expect is that sync.Pool() wou
by gwd 2y ago
> That's what the New function does, isn't it?
But that's only run when the pool needs to allocate more space. What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does.
I think Golang's implementation does make sense, as sync.Pool() is clearly an optimization you use when performance is an issue; and in that case you almost certainly want to only initialize parts of the struct that are needed. But I can see why it would be surprising.
> [any] is a type
It's typed the way Python is typed, not the way Rust or C are typed; so loses the "if it compiles there's a good chance it's correct" property that people want from statically typed languages.
I don't use sync.Pool, but it does seem like now that we have generics, having a typed pool would be better.
- 9rx 2y ago> so loses the "if it compiles there's a good chance it's correct" property that people want from statically typed languages. If that's what people actually wanted, Coq and friends would be household names, not the obscure oddities that they are. All the languages that people actually use on any kind of regular basis require you to write tests in order to gain that sense of correctness, which also ends up validating type-correctness as a natural byproduct. "A machine can help me refactor my code" is the property that most attracts people to the statically typed languages that are normally used. With "I can write posts about it on the internet" being the secondary property of interest.
- gwd 2y agoIt's a spectrum, with costs and benefits at each level. I lock my front door even though I don't have bars on my windows; I prefer Golang, where doing a basic compile will catch a fair number of errors and testing will catch the rest, to Python or Perl where testing is the only way to catch errors.
- 9rx 2y ago> where doing a basic compile will catch a fair number of errors In the case of refactoring this is incredibly useful. It doesn't say much about the correctness of your program, though.
- tgv 2y ago> What GP seems to expect is that sync.Pool() would always return a zeroed structure Might be, but that's a design decision that has nothing to do with type or generics, isn't it? You seem to refer to a function to drain the pool, which is not needed, and frankly, rather unusual. > It's typed the way Python is typed Not in the slightest. > "if it compiles there's a good chance it's correct" If you want to compare it to something, it's more like Rust's unwrap(), which will panic if you apply it to the wrong result.
- gwd 2y ago> Not in the slightest. You know, it's this kind of comment on USENET forums which prompted the creation of StackOverflow. It's not curious and adds nothing to the discussion. I like Go and use it extensively; and I like having the option to fall back to the `any` type. But it's simply a fact that using the `any` type means that certain properties of the program can't be checked at compile time, in the same way that Python isn't able to check certain properties of the program at compile time. > If you want to compare it to something, it's more like Rust's unwrap(), which will panic if you apply it to the wrong result. Rust's unwrap() is used when a type can have one of exactly two underlying types (which is why no type is specified). In this case, getting back an `any` type means the underlying type could literally be anything -- as demonstrated by the example, where they put an integer, a string, and an empty struct into the pool. That's almost certainly not what you wanted, but the compiler won't prevent you from doing it.
- tgv 2y agoSorry, but comparing Python's total absence of typing to extracting a value from any is quite weird. > certain properties of the program can't be checked at compile time Neither can you check if a number is positive or negative, or if a string is empty or not at compile time, but that doesn't make Go similar to COBOL or Forth. `var v any` declares v to be of the type any, not of any arbitrary type, which is what Python does. Writing `v + 1` gives a compiler error, unlike Python, which may or may not turn it into a runtime error. It is rather different, and especially so when you look at interfacing. Even though you may declare a variable to be an integer in Python, there is no guarantee that it actually is, whereas in Go that is the case, which has significant implications for how you handle e.g. json. > the compiler won't prevent you from doing it. It will prevent you from using e.g. an array of strings as if it were an array ints. Python does not. They are quite different. > You know, it's this kind of comment on USENET forums which prompted the creation of StackOverflow. It's not curious and adds nothing to the discussion. Ffs.
- ignoramous 2y ago> What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does. One could define a new "Pool[T]" type (extending sync.Pool) to get these guarantees: type Pool[T any] sync.Pool // typed def func (p *Pool[T]) Get() T { // typed Get pp := (*sync.Pool)(p) return pp.Get().(T) } func (p *Pool[T]) Put(v T) { // typed Put pp := (*sync.Pool)(p) pp.Put(v) } intpool := Pool[int]{ // alias New New: func() any { var zz int; return zz }, } boolpool := Pool[bool]{ // alias New New: func() any { var zz bool; return zz }, } https://go.dev/play/p/-WG7E-CVXHR https://go.dev/play/p/-WG7E-CVXHR
- deleted 2y ago[deleted]
- 9rx 2y ago> One could define a new "Pool[T]" type (extending sync.Pool) to get these guarantees: So long as that one is not you? You completely forgot to address the expectation: type Foo struct{ V int } pool := Pool[*Foo]{ // Your Pool type. New: func() any { return new(Foo) }, } a := pool.Get() a.V = 10 pool.Put(a) b := pool.Get() fmt.Println(b.V) // Prints: 10; 0 was expected.
- ignoramous 2y ago> You completely forgot to address the expectation > fmt.Println(b.V) // Prints: 10; 0 was expected. Sorry, I don't get what else one expects when pooling pointers to a type? In fact, pooling *[]uint8 or *[]byte is common place; Pool.Put() or Pool.Get() then must zero its contents.
- 9rx 2y ago> I don't get what else one expects when pooling pointers to a type? As seen in your previous comment, the expectation is that the zero value will always be returned: "What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does." To which you offered a guarantee. > Pool.Put() or Pool.Get() then must zero its contents. Right. That is the solution (as was also echoed in the top comment in this thread) if one needs that expectation to hold. But you completely forgot to do it, which questions what your code was for? It behaves exactly the same as sync.Pool itself... And, unfortunately, doesn't even get the generic constraints right, as demonstrated with the int and bool examples.
- stouset 2y ago> But that's only run when the pool needs to allocate more space. What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does. Not quite that. Imagine I have a pool of buffers with a length and capacity, say when writing code to handle receiving data from the network. When I put one of those buffers back, I would like the next user of that buffer to get it back emptied. The capacity should stay the same, but the length should be zero. I think it’s reasonable to have a callback to do this. One, it doesn’t force every consumer of the pool to have to remember themselves; it’s now a guarantee of the system itself. Two, it’s not much work but it does prevent me from re-emptying freshly-allocated items (in this case reinitialzing is fast, but in some cases it may not be). This also should be an optional callback since there are many cases where you don’t want any form of object reset.