[HN Gopher] Proposal: expression to create pointer to simple types
___________________________________________________________________
Proposal: expression to create pointer to simple types
Author : jgrimm
Score : 123 points
Date : 2021-04-19 13:07 UTC (9 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| w-m wrote:
| How exemplary that he filled in the full template questionnaire
| for language changes, including questions such as "Would you
| consider yourself a novice, intermediate, or experienced Go
| programmer?" (he replied "I have some experience").
| Waterluvian wrote:
| There's the General who responds, "You don't need to see my ID.
| Don't you know who I am? Who's your CO?!"
|
| And the General who responds, "Right. Well done."
| not_knuth wrote:
| IIRC Tim Berners-Lee also rather amusingly called himself a
| "Web Developer" at a conference.
| gowld wrote:
| He did develop the web.
| furyofantares wrote:
| There's a double connotation being played with there, a web
| developer vs the web developer
| c3534l wrote:
| Yeah, I think that was the "rather amusingly" part.
| tempest_ wrote:
| I found it more amusing that he listed 8 languages that he has
| experience with and then went out of his way to exclude
| JavaScript.
| Simplicitas wrote:
| Is JavaScript a language? <grin>
| cxr wrote:
| Please don't use HN comments for posting low-quality jokes,
| even if (perhaps "especially if") they're considered
| acceptable/appropriate for other communities.
| fuzzer37 wrote:
| I thought it was funny
| [deleted]
| tssva wrote:
| Can you point to some specific guidelines used to judge
| low-quality vs high-quality jokes for HN purposes? Or are
| we suppose to go by your personal and subjective opinion
| of low-quality vs high-quality comedy material?
| Graffur wrote:
| In this case it seems pretty clear. If you're not sure
| you could try reading more posts before posting. You
| should be able to pick up the general vibe/tone.
| tssva wrote:
| I found the comment funny. Since the purpose of a joke is
| to be funny this makes it high quality as far as I am
| concerned. If you didn't find it funny, I guess you would
| consider it low quality. The point is that judging comedy
| and jokes is so subjective and personal as to make any
| such rule regarding quality a joke itself. You can ban
| all jokes or mandate that jokes can't contain racist,
| sexist, ageist, or other such potentially offensive
| content but trying to filter jokes based upon quality is
| a bit ridiculous.
| throwaway19937 wrote:
| AFAIK there aren't specific guidelines but posts like the
| grandparent are normally moderated down.
| (https://news.ycombinator.com/item?id=2965166) is an
| example of well-received humor; it's one of the top rated
| posts.
| anon_tor_12345 wrote:
| are there hn hall monitors that I'm not aware of? like
| why are you policing people's jokes? For the life of me I
| will never understand why people voluntarily take on the
| mantle of authoritarian. Do you feel like you're
| contributing to something by censuring someone for a
| joke?
| aloisdg wrote:
| When you know JavaScript enough to be able to say that you
| don't know JavaScript.
| zibzab wrote:
| That's basically me and C++, and I suspect I am not alone
| jasonwatkinspdx wrote:
| A couple years ago there was a clear shift in how the highest
| profile people in go core interact with the community. In the
| past it felt a bit one way. I have no idea what triggered the
| change, but it's definitely visible, and hugely positive imo.
| vanderZwan wrote:
| > _Is this about generics?_
|
| > No.
|
| Not sure why, but something about this being the (first part of
| the) last question he had to answer makes it quite funny to me.
| marcosdumay wrote:
| When you have two questions about a specific feature that
| every change proposal must answer, it's a sign that something
| needs improvement.
| [deleted]
| [deleted]
| mohaine wrote:
| Personally, I like the 100% correct answer to
|
| Can you describe a possible implementation? (Yes.)
| nicklecompte wrote:
| Also good:
|
| > What would change in the language spec?
|
| > The new operator would get an optional second argument,
| and/or conversions would become addressible.
|
| > [...]
|
| > How would the language spec change?
|
| > Answered above. Why is this question here twice?
| phoe-krk wrote:
| _> (...) that has the nasty problem that 3 does not have a type
| (...)_
|
| How is that possible? At least in Common Lisp, all literal
| objects have types, and the same is true of C from what I have
| just checked.
| Smaug123 wrote:
| It doesn't have a type in Haskell, from a certain point of
| view. `3` is polymorphic.
|
| Prelude> :t 3
|
| 3 :: Num p => p
| tome wrote:
| It does have a type. You just wrote it down!
| Smaug123 wrote:
| Hence "from a certain point of view" - I would argue, in
| fact, that it's extremely similar to the sense in which 3
| doesn't have a type in Go. Haskell's type system can
| express that sense, whereas Go's can't; but it's the same
| sense.
|
| > It is an error if the constant value cannot be
| represented as a value of the respective type. An untyped
| constant has a default type which is the type to which the
| constant is implicitly converted in contexts where a typed
| value is required, for instance, in a short variable
| declaration such as i := 0 where there is no explicit type.
| The default type of an untyped constant is bool, rune, int,
| float64, complex128 or string respectively, depending on
| whether it is a boolean, rune, integer, floating-point,
| complex, or string constant.
| earhart wrote:
| Go supports untyped constants --
| https://golang.org/ref/spec#Constants. It's useful for defining
| a named constant, and then using the name to initialize
| variable values of any compatible type.
| haolez wrote:
| > What other languages do you have experience with?
|
| > Fortran, C, Forth, Basic, C, C++, Java, Python, and probably
| more. Just not JavaScript
|
| Forth is a "top-of-mind" language for Rob Pike. That's unexpected
| and incredibly cool :)
| candiddevmike wrote:
| I like the second option (&int(3)) the most personally, as I find
| myself occasionally defining a bunch of variables before I can
| use them as pointers in structs. It looks and feels a lot cleaner
| to use this vs having new everywhere.
| cassianoleal wrote:
| That would be my preference too. Great readability, and most
| users would eventually try this out even before searching for
| the right way (I have tried it).
|
| I tend to not declare variables when the pointer is used deep
| into a struct because I find the back-and-forth in the editor
| to be bad. I usually resort to a pointer to an inline anonymous
| function, e.g.: a := SomeStruct{
| Field: func() *int64 { x := int64(13); return &x }(), }
|
| It's ugly and verbose but after seeing it 2 or 3 times you
| immediately know what it's about the next time.
| kevincox wrote:
| I was surprised that you can't apply & to any value. I thought it
| was gut an ordinary operator and it would ensure that the value
| it was applied to would be put onto the heap. s
| := S{} sp := &s // Works _ = sp _ = &S{}
| // Works i := int32(1) ip := &i // Works!
| _ = ip _ = &int32(1) // Doesn't work!
|
| https://play.golang.org/p/fdgvbEwJWgh
|
| It seems odd that you can't apply & to a function's return value.
| I think the best approach would be making & work in basically any
| scenario. For example the following also doesn't currently work.
| &(int32(1) + int32(1))
|
| It seems like it should be possible to "desugar" &X to `_tmp = X;
| &_tmp` and solve this weirdness.
| jerf wrote:
| One of the things I think the Go tutorials don't make a big
| enough deal of is that Go is relatively explicit about
| allocations. := isn't just a shortcut for declaring variables,
| it's an allocation, and an error to use it when it doesn't
| allocate. var X Sometype isn't just a declaration, it's an
| allocation.
|
| := kinda smears the clarity by not allocating if you have a
| variable on the left that is already allocated, and there's
| some other places where it kinda smears things up, but at the
| core, Go makes you explicitly allocate.
| gowld wrote:
| I think the way to say it is that Go requires you to declare
| every allocation, but allows over-declaration in the case of
| copying.
|
| > := [...] an error to use it when it doesn't allocate.
|
| > := [...] not allocating if you have a variable on the left
| that is already allocated,
|
| This appears to be a contradiction.
|
| I suppose you mean something like "error to use it when
| there's no possible context where that line of code would
| allocate"; what's an example of that?
| fred256 wrote:
| a, b := 1, 2
|
| If either a or b (but not both) were already defined, this
| won't re-define (and reallocate space for) them.
| gowld wrote:
| Aha, lossy compression syntax!
| catlifeonmars wrote:
| I'm not sure I understood this correctly. Does the following
| allocate (on the heap)? foo := MyStruct{}
| coder543 wrote:
| No, that does not cause a heap allocation on its own. If
| other lines of code in that function caused a pointer to
| that value to escape the lifetime of the current function's
| stack frame, the compiler would determine that it has to be
| heap allocated instead.
|
| I believe the person you are replying to was making a
| confusing point about some hand wavy notion of "any kind of
| allocation", which includes stack allocations... which are
| determined at compile time, not with "alloca".
| catlifeonmars wrote:
| Ah, that makes much more sense!
| kevincox wrote:
| No. In this case foo will live on the stack (unless you
| take its address later). foo := MyStruct{}
| // Could live on the stack _ := &foo // Oh, now foo
| must live on the heap.
| coder543 wrote:
| This isn't even true.
|
| No matter what syntax you write inside a function, the Go
| compiler always has the final say on what is stack
| allocated and what is heap allocated. Taking the address
| of foo will not cause foo to be heap allocated unless Go
| is unable to prove that the pointer will live for less
| time than the current stack frame. Look up "escape
| analysis".
|
| Basically the only way to _guarantee_ that something will
| always be heap allocated is to assign it to a global
| variable. Even returning a pointer to that object from
| the current function is not a strong guarantee, since the
| compiler could inline this function into the caller and
| determine that everything can live happily inside the
| newly inlined stack frame without heap allocation.
| kevincox wrote:
| Good point. It isn't "must", I should have said "may".
| coder543 wrote:
| > it's an allocation
|
| Most people probably think "heap allocation" when you say
| this. Go doesn't do dynamic allocations within a stack frame
| (alloca in C), so when you say "it's an allocation", what
| does that mean? It could be a stack allocation that occurred
| at compile time as a reservation in the size of the stack
| frame for that function. It could be a heap allocation. Only
| the compiler knows!
|
| The Go compiler is the ultimate authority on what becomes a
| heap allocation. It tries to make everything into a stack
| allocation when possible, and stack allocations are "free".
|
| Beyond that, a sufficiently smart compiler can reuse stack
| "allocations" within a single function as certain values
| become "dead" (never used again). So there isn't even
| guaranteed to be a 1:1 correspondence between "stack
| allocations" and variables that you declared inside the
| function.
|
| So, I completely disagree with your statement about Go being
| "relatively explicit about allocations." It's one of the
| least explicit compiled languages in that regard.
|
| Go makes a distinction between declaration and assignment,
| which is the syntax you're talking about. It really has
| nothing to do with allocations.
| jerf wrote:
| You file a good complaint, and I should clarify. What I
| mean is more like Go is _secretly_ quite explicit about its
| allocations, if I may. It _superficially_ looks like it
| doesn 't really care with a variety of syntax glosses that
| can make it look like it's more like a scripting language
| where it doesn't care, but it actually does care quite a
| lot even at the syntax level, and if you dig past the
| syntax glosses, it is actually explicit about what gets
| allocated. It doesn't successfully hide it from you like a
| scripting language does.
|
| Also, allocations are just... allocations. Go _qua_ Go
| doesn 't have stack vs. heap, and it's a mistake to care
| except when optimizing. So in Go _qua_ Go, it isn 't an
| issue that it may "reuse" a particular address, because in
| Go _qua_ Go you can 't witness that anyhow. (If you try to
| keep a pointer around to witness it with, you'll keep the
| thing pointed to alive.) From Go's perspective, it's still
| an allocation even if the implementation manages to re-use
| a particular memory address to do so.
|
| I'm talking about the runtime Go implements here, not the
| implementation.
|
| This actually took me some _years_ to correctly
| internalize, for what it 's worth. It does a "good" job of
| glossing over things. However, if you really poke at it,
| allocations are still explicit. They just may not look like
| what you are used to from other languages.
| kevincox wrote:
| I don't see how: temp := someFunc() p
| := &temp
|
| is any less explicit than p := &someFunc()
|
| It seems that the `&` is still required to put something onto
| the heap.
| zeeboo wrote:
| What about `&m[x]` where m is some map? Does that heap
| allocate and create a copy, or is it a pointer to the
| actual storage slot? If the former, that's a hidden
| copy/allocation that didn't exist before, and if it's the
| latter, resizing the map invalidates the pointer, so it
| must be updated somehow.
| kevincox wrote:
| `&` will "move" something to the heap if it isn't already
| on the heap.
|
| The simpler way to think about it is that in Golang
| everything is on the heap. However the optimizer will
| move things to the stack if they don't have their address
| taken. I think the point about explicitness is that if
| you don't use `&` then it will be able to be put on the
| stack. So `&` doesn't cause a heap allocation but lack of
| `&` (or new()) confirms that there isn't one. (I don't
| actually know if that is true but I can't think of any
| counterexamples)
| zeeboo wrote:
| I think I didn't communicate my point clearly. Consider
| this hypothetical program: x :=
| make(map[int]int) x[0] = 5 y :=
| &x[0] *y = 10 print(x[0]) // 5
| or 10? x[0] = 6
| print(*y) // 6 or 10? // force the map
| to grow and reallocate the buckets for j := 1; j
| < 100; j++ { x[j] = j } *y =
| 11 print(x[0]) // 5, 6, 10, or 11?
|
| The crux of the problem is answering what y actually
| points at: the value in the map bucket, or some freshly
| allocated value? There are problems with whichever one
| you pick.
|
| edit: changed the second print to *y instead of x[0].
| thanks masklinn for catching this error.
| masklinn wrote:
| > print(x[0]) // 5, 6 or 10?
|
| Do you mean `print(*y)`? You just assigned to `x[0]` so
| its value should not be in question.
|
| Also
|
| > if it's the latter, resizing the map invalidates the
| pointer, so it must be updated somehow.
|
| It doesn't (have to) invalidate the pointer though. When
| resized the map's content get copied to a new backing
| buffer, the pointer can keep pointing to the old buffer.
| That's basically the same behaviour as slices: when a
| slice resizes, a new backing array is allocated, the
| contents get copied to the new array, and the slice is
| retargeted to the new array. There can be other slices
| pointing to the old array (it's of course a very bad idea
| to update slices to shared arrays, but Go will let you do
| it).
| zeeboo wrote:
| > It doesn't (have to) invalidate the pointer though.
| When resized the map's content get copied to a new
| backing buffer, the pointer can keep pointing to the old
| buffer.
|
| That's true, but I don't think it's very comparable to
| slices. With slices, you have to explicitly reallocate
| either by creating a whole new slice or using append.
| Reslicing, indexing, or other operations do not
| reallocate. On the other hand, maps may end up resizing
| on any operation that involves them, or even
| theoretically in the background without any operations
| (during GC, for example). It would be unfortunate to lose
| that implementation flexibility, and keeping it means
| that you're essentially picking the "make a copy" option.
| masklinn wrote:
| > That's true, but I don't think it's very comparable to
| slices
|
| It's exactly the same.
|
| > With slices, you have to explicitly reallocate either
| by creating a whole new slice or using append.
|
| That's a distinction without a difference. `append` does
| not "explicitly reallocate", it may or may not
| reallocate, you've no idea. Even if the backing array is
| full, it might be realloc'd in-place.
|
| > On the other hand, maps may end up resizing on any
| operation that involves them, or even theoretically in
| the background without any operations (during GC, for
| example).
|
| So?
|
| Also technically nothing prevents a GC from reallocating
| the slice.
|
| > It would be unfortunate to lose that implementation
| flexibility, and keeping it means that you're essentially
| picking the "make a copy" option.
|
| I've never heard of a hashmap implementation which would
| do otherwise.
|
| Trying to extend in-place and attempting to properly
| redistribute if that works sounds like absolute hell.
| Likewise trying to shrink in-place, though at least
| you've got some scratch space which you don't have in the
| other case: you'd have to segregate everything into one
| half of the map then insert them in the other half,
| before shrinking your allocation, which might give you a
| new allocation anyway, at which point you've moved all
| your values thrice whereas just creating a new allocation
| and reinserting your stuff there is a single move.
| zeeboo wrote:
| > That's a distinction without a difference. `append`
| does not "explicitly reallocate", it may or may not
| reallocate, you've no idea. Even if the backing array is
| full, it might be realloc'd in-place.
|
| Maybe to you, but to me, a pointer going from modifying
| the value inside of the map to no longer modifying the
| value inside of the map during any operation is quite a
| bit different than requiring a reassignment of the slice
| header. In other words: x :=
| make([]int, 5) y := &x[0] x[3] = 8
| *y = 5 print(x[0]) // always prints 5
|
| as compared to x := make(map[int]int)
| y = &x[0] // btw, is this even valid? let's assume it
| implicitly does x[0] = 0 x[3] = 8 *y = 5
| print(x[0]) // maybe sometimes prints 5?
|
| is meaningfully different. For slices, we know that x[0]
| will always print 5 until the value of x is reassigned in
| some way.
|
| > Also technically nothing prevents a GC from
| reallocating the slice.
|
| It would have the same problem the map does: you'd have
| to update any pointers into the slice to point to the new
| slice, otherwise the semantics of the program changes.
| That is not something the GC currently does, and would
| require an awful lot of metadata and scanning.
|
| > I've never heard of a hashmap implementation which
| would do otherwise.
|
| I'm not sure what this is referring to. I agree every map
| implementation has to reallocate the backing store of
| values periodically. I was trying to say that keeping the
| flexibility to reallocate the backing store of the map
| during GC means that you cannot choose the "writes
| through pointer are observed in the map" option (at least
| without a lot of complication around updating pointers)
| because as a programmer, you would not be able to know if
| it would do that or not, which is a fairly useless
| primitive.
| masklinn wrote:
| > Maybe to you
|
| Yes, I avoid making assumptions about invariants across
| mutation calls, that's just a bad idea.
|
| > For slices, we know that x[0] will always print 5 until
| the value of x is reassigned in some way.
|
| Unless an other goroutine is stomping on your backing
| array anyway.
|
| > It would have the same problem the map does: you'd have
| to update any pointers into the slice to point to the new
| slice, otherwise the semantics of the program changes.
| That is not something the GC currently does, and would
| require an awful lot of metadata and scanning.
|
| Yes. So maybe we could ignore that useless strawman?
|
| > I'm not sure what this is referring to.
|
| To what I'm quoting.
|
| > I was trying to say that keeping the flexibility to
| reallocate the backing store of the map during GC
|
| That sounds less like flexibility and more like "let's
| make the GC slower and more complex for no reason".
| zeeboo wrote:
| I apologize if the tone of my previous comment sounded
| harsh to you or if some of my arguments sounded like
| strawmen. I am in good faith trying to interpret your
| comments as best as I am able. I don't feel like you're
| giving me the same courtesy, so I'll exit the discussion
| now. Thanks.
| masklinn wrote:
| > So `&` doesn't cause a heap allocation but lack of `&`
| (or new()) confirms that there isn't one. (I don't
| actually know if that is true but I can't think of any
| counterexamples)
|
| I think assigning to a pointer would cause an escape.
|
| Just taking a reference wouldn't though, the reference
| still has to escape (of course you'd usually take a
| reference so that it can escape but that's not always the
| case, especially with inlining).
| kevincox wrote:
| What do you mean by assigning to a pointer? You can only
| assign a pointer value to a pointer variable and you need
| to get that pointer from & IIUC.
| masklinn wrote:
| > What do you mean by assigning to a pointer?
| *x = y
| kevincox wrote:
| I don't think that does because IIUC you are copying the
| bits of y to x. So I guess semantically y has escaped but
| you aren't doing a new heap allocation, you are reusing
| the memory of x.
| iovec wrote:
| Since it doesn't seem like this was answered in the other
| discussion, the answer is that Go does not allow taking
| the address of a map value. You get a compile-time error:
| "cannot take the address of m[x]".
|
| https://play.golang.org/p/rX8A6ez9fVx
| zeeboo wrote:
| Indeed. This is in a thread where the original comment
| was "I think the best approach would be making & work in
| basically any scenario." I'm trying to demonstrate the
| complications of making it work on map accesses.
| [deleted]
| liendolucas wrote:
| >It seems odd that you can't apply & to a function's return
| value.
|
| Offtopic: Surprisingly I was asking myself this question but if
| possible in C... Is it?
| fanf2 wrote:
| Yes, if f() returns the type T then you can write
| &(T[]){ f() }
|
| The type in brackets needs to be an array so that if f()
| returns a struct then the initializer list has the right
| shape. If T is a simple type then you can drop the [].
| gowld wrote:
| Nope. You can only take a reference to an lvalue, which is
| (essentially) an expression that is legal to use in the form
| `my_lvalue = .... Otherwise, there's nothing to take the
| reference of. int* ref1() { return &1; }
| -> error: lvalue required as unary '&' operand
| // #include <stdlib.h> int alloc() {
| return *(int*)malloc(sizeof(int)); } int*
| ref() { return &alloc(); } -> error: lvalue
| required as unary '&' operand
|
| You can still be unsafe though, by making a reference to a
| stack-allocated object and letting it go out of scope :
| int* make_unsafe_ref() { int a; return &a; } ->
| warning: function returns address of local variable
| masklinn wrote:
| > You can only take a reference to an lvalue, which is
| (essentially) an expression that is legal to use in the
| form `my_lvalue = ....
|
| I mean it _could_ implicitly allocate, that 's what Rust
| does for instance.
|
| Your second and third attempts would not compile though,
| the first would by returning a `&'static T`.
| steveklabnik wrote:
| "implicitly allocate" is slightly misleading, imho. It's
| promoted to a static. There's no malloc involved.
| masklinn wrote:
| > "implicitly allocate" is slightly misleading
|
| Maybe. I just meant that storage is created implicitly
| (static or stackframe depending on the case), then a
| reference is created to _that_.,
| steveklabnik wrote:
| Totally, I don't think you're _wrong_ , just like, people
| read "allocate" in different ways. I wish words were
| clearer, heh.
| kevincox wrote:
| No, but in C you can't apply `&` to any stack value and
| "automagically" pop it onto the heap. Or from another point
| of view everything in Go is logically on the heap, the
| compiler just optimizes values that don't have their address
| taken to live on the stack.
|
| In C: int *f() { int x = 0;
| return &x; }
|
| It works, but it is wrong. The C type system isn't smart
| enough to realize the lifetime of x in this case. It is not
| allowed for a function return because C does have the concept
| of a temporary value so it is disallowed because it is
| basically always incorrect to do so.
|
| Note that C++ does somewhat allow this with lifetime
| extension. It is somewhat like what I expected Go to do,
| except because lifetime extension only extends to the
| enclosing block it is more of a footgun. With a dynamic
| tracing garbage collector like Go it not a footgun.
| anonacct38 wrote:
| & doesn't always imply a value is on the heap. Escape analysis
| will ensure that pointers to the stack are safe.
|
| Here's an example with a bit of explanation: if you pass a
| value to fmt.Println it will escape. The raw println builtin
| does not cause values to escape. So calling the first function
| twice and seeing the same address for the value strongly
| implies stack allocation while calling the 2nd function twice
| and getting different addresses implies heap allocation.
|
| https://play.golang.org/p/PSb1wj1-x1c
| effdee wrote:
| Minutes ago I was wondering about
| https://play.golang.org/p/9C0puRUstrP (via
| https://github.com/golang/go/issues/23440).
|
| Thank you for explaining what's going on there! :)
|
| And btw, compiling the above example with -gcflags="-m"
| (which I learned about earlier today) proves you right.
| Laremere wrote:
| I asked that in the Github issue, response here:
| https://github.com/golang/go/issues/45624#issuecomment-82259...
| With my reply (and further exploration) here:
| https://github.com/golang/go/issues/45624#issuecomment-82263...
| dang- wrote:
| cxr, I've been watching you for years, and you've got potential.
| You inspire me to be a better mod. If you're willing to take The
| Oath, you can be be part of the team enjoy the following
| benefits:
|
| * Freedom to shitpost on as many alts as you like.
|
| * Ever heard of a double upvote? Or a triple flag? Now you have.
|
| * Monthly yoga and mindfulness with pg (you are _not_ to make eye
| contact).
|
| Fully understood if this is too great a responsibility, but you
| are truly one with the spirit of HN and its Guidelines.
| whateveracct wrote:
| I have so many BS helpers in my project to do this
| util.StrPtr("hello") util.BoolPtr(false)
| util.Int64Ptr(7) // etc
|
| This is just a gap in Go's design, so I'm glad this proposal
| exists :)
| gher-shyu3i wrote:
| Generics would solve the issue.
| Groxx wrote:
| Well. It would reduce it to 1 (PtrTo) instead of [however
| many]. And unless they also add a new top-level func like
| that, it's still a `package.PtrTo` rather than `&`. And `&`'s
| special abilities on only composite literals remains.
| majewsky wrote:
| Not entirely. Someone in the comments of the issue suggests
| to implement this with generics as: func
| PointerOf[T any](t T) *T { return &t }
|
| But that has a nasty gotcha: func Process(x
| *int32) { if (x != nil) { fmt.Println(*x +
| 5); } } func main() {
| Process(nil); //ok x := i32(5)
| Process(&x); //ok Process(PointerOf(5));
| //BOOM: cannot use PointerOf(5) (value of
| //type *int) as *int32 value in argument to Process }
|
| Go's type coercion is quite primitive. It strictly works
| inside-out (propagating types strictly upwards in the AST),
| with the only exception that a numeric literal can be coerced
| into a specific numeric type by considering the immediate
| parent in the AST. So when you have `func f(x int32)` and you
| call it as `f(5)`, the literal 5 gets coerced into int32 to
| match the context it appears in. (The same strategy is also
| applied to determine the type of a nil literal.)
|
| However, in `Process(PointerOf(5))`, the immediate
| surrounding of the literal 5 (the PointerOf call) does not
| coerce the literal into a specific type, so it takes on its
| default type, int.
|
| The proposal (or, to be exact, both proposals) avoids this
| gotcha by requiring a type to be stated explicitly.
| Process(new(i32, 5)); Process(&i32(5));
| darksaints wrote:
| For a language that has taken extreme measures to exclude
| generics because they are deemed to complex, this proposal is
| absolutely surprising to me. And I'm still not sure what
| practical benefit comes from it.
| coldtea wrote:
| Practical benefit in the sense of "you get a free pony" or
| "before you didn't have closures and now you do"? None.
|
| Practical benefit in the sense of "you get to express something
| in a shorter, more uniform way"? Some.
| jerf wrote:
| I'm not sure why this would be posted to HN, honestly; it's a
| very "inside baseball" thing. In my personal experience this
| would save a lot less than one line per module. I've
| encountered this, but it's infrequent. The benefit is very
| minimal in practice.
| emodendroket wrote:
| Imagine you want to create an instance of a struct with many
| string and int pointer fields. This is actually a big pain in
| the ass in Go (the AWS SDK offers an aws.String helper for this
| reason).
| dpatterbee wrote:
| For a language that has taken extreme measures to exclude
| generics, Go seems to have an awful lot of accepted design
| proposals for generics.
| sylvinus wrote:
| Best part of this proposal from Go's co-creator:
|
| > Would you consider yourself a novice, intermediate, or
| experienced Go programmer?
|
| I have some experience.
| bigdubs wrote:
| It's interesting that this can be largely implemented oneself
| once type parameters are part of the language (as one thread
| commenter pointed out with `PointerOf(t T) *T`), I'm curious what
| other syntactical oddities become a thing of the past once we can
| create more expressive and typesafe functions for common kludges.
| dang- wrote:
| Please don't use HN comments for posting low-quality jokes, even
| if (perhaps "especially if") they're considered
| acceptable/appropriate for other communities.
| wejick wrote:
| >How would we measure it? >Eyeballing.
|
| Love this one
| dang- wrote:
| Please don't use HN comments for posting low-quality jokes,
| even if (perhaps "especially if") they're considered
| acceptable/appropriate for other communities.
| uh_uh wrote:
| Why can't &3 work? Rob says 3 does not have a type and that's a
| problem. Would it be possible to change the Go compiler such that
| 3 has a type? (I'm guessing no, at least not easily, otherwise
| he'd be suggesting it, but I'm curious about the reason)
| fred256 wrote:
| From the language specification:
|
| > Numeric constants represent exact values of arbitrary
| precision and do not overflow.
| friseurtermin wrote:
| Ok I think I'm not understanding this correctly then. Why
| does this return an error? package main
| import ( "fmt" ) const
| tst = 1000000000000000000000000000000000000000000000000000000
| 000000000000000000000000000000000000000000000000000000000
| func main() { fmt.Println("%v", tst) }
|
| Error: ./prog.go:16:17: constant 1000000000000000000000000000
| 0000000000000000000000000000000000000000000000000000000000000
| 00000000000000000000000 overflows int
|
| See: https://play.golang.org/p/47l5qAsXD5r
| howeyc wrote:
| https://golang.org/ref/spec#Constants
|
| > An untyped constant has a default type which is the type
| to which the constant is implicitly converted in contexts
| where a typed value is required.
|
| The default type for a number (integer) is int. If you were
| to add a period in that long string of zeros, the default
| for floating-point is float64.
| HALtheWise wrote:
| If you look closely, that error happens at the print line
| statement, not at the constant declaration. The constant is
| perfectly legal to exist as written, and you can even do
| constant operations like mathematics on it, you just can't
| legally assign it to a variable (implicitly in this case as
| part of the function call) if it's value is too large for
| the type of that variable.
| masklinn wrote:
| > Would it be possible to change the Go compiler such that 3
| has a type? (I'm guessing no, at least not easily, otherwise
| he'd be suggesting it, but I'm curious about the reason)
|
| Why not? In fact it already kind-of does: Go has "default
| types" for most untyped constants. When you write
| i := 3
|
| absent an explicit type, Go will fall back to "int".
| Laremere wrote:
| I asked that in the thread, response here:
| https://github.com/golang/go/issues/45624#issuecomment-82259...
| With my reply (and further exploration) here:
| https://github.com/golang/go/issues/45624#issuecomment-82263...
|
| tl;dr: Go uses casts to coerce values to the correct types,
| which means you couldn't get pointers to number literals for
| non-default number types.
| majewsky wrote:
| If the syntax were &3, you could use it to obtain a *int, but
| not a *int32, *uint64 or such.
| nemo1618 wrote:
| Personally, I've only seen this crop up in one place: structs
| that signal optional fields via pointers. Are there other
| usecases?
| the-dude wrote:
| Can I create a pointer to a pointer?
| jerf wrote:
| Yes, however the utility of doing so in Go is fairly limited.
| Go has "pointers" but doesn't have pointer arithmetic, and the
| big use case of pointer-to-pointer in C is to iterate over an
| array of pointers via pointer arithmetic. Personally I would
| call them "references" since I consider pointer _arithmetic_ to
| be the thing that makes pointers _pointers_ and not just
| references, but that 's a personal opinion, not a universally-
| agreed-upon definition.
| masklinn wrote:
| An other big use-case for pointers to pointers in C is
| pointer-type out parameters. _Most_ of that use-case is
| handled by MRV, but I 'm pretty sure there's the odd
| situation where a double pointer is either necessary or
| convenient (I remember seeing the odd one in Rust once in a
| while).
| forgetfulness wrote:
| Pointer-pointers are nice to implement linked data structures
| in C; they make a lot of logic surrounding re-seating the
| head pointer far simpler and with fewer edge cases.
| majewsky wrote:
| This reminds me of the old adage that the level of
| experience of C developers can be ranked into 1 star, 2
| stars, 3 stars and so on, based on the highest number of
| consecutive stars they use in type expressions.
| fanf2 wrote:
| This reminds me a lot of C99 compound literals: one of the Go
| suggestions looks like &int(3) which in C99 is spelled &(int){ 3
| }.
|
| (I was slightly surprised when I learned that C99 compound
| literals are not just for structs: you can use any complete
| object type, and the result is an lvalue so you can take its
| address.)
___________________________________________________________________
(page generated 2021-04-19 23:03 UTC)